diff --git a/src/facade/command_id.h b/src/facade/command_id.h index 5649aadac..917ca1081 100644 --- a/src/facade/command_id.h +++ b/src/facade/command_id.h @@ -7,6 +7,7 @@ #include #include "facade/facade_types.h" +#include "server/acl/acl_commands_def.h" namespace facade { @@ -30,9 +31,10 @@ class CommandId { * -1 means the last key index is (arg_length - 1), -2 means that the last key * index is (arg_length - 2). * @param step - step count for locating repeating keys + * @param acl_categories - bitfield for acl categories of the command */ CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, int8_t last_key, - int8_t step); + int8_t step, uint32_t acl_categories); std::string_view name() const { return name_; @@ -58,6 +60,10 @@ class CommandId { return step_key_; } + uint32_t acl_categories() const { + return acl_categories_; + } + static uint32_t OptCount(uint32_t mask); protected: @@ -68,6 +74,7 @@ class CommandId { int8_t first_key_; int8_t last_key_; int8_t step_key_; + uint32_t acl_categories_; }; } // namespace facade diff --git a/src/facade/facade.cc b/src/facade/facade.cc index cdbc97c41..5f62bc1e7 100644 --- a/src/facade/facade.cc +++ b/src/facade/facade.cc @@ -140,9 +140,9 @@ RedisReplyBuilder* ConnectionContext::operator->() { } CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, - int8_t last_key, int8_t step) + int8_t last_key, int8_t step, uint32_t acl_categories) : name_(name), opt_mask_(mask), arity_(arity), first_key_(first_key), last_key_(last_key), - step_key_(step) { + step_key_(step), acl_categories_(acl_categories) { } uint32_t CommandId::OptCount(uint32_t mask) { diff --git a/src/server/acl/acl_commands_def.h b/src/server/acl/acl_commands_def.h new file mode 100644 index 000000000..92241d779 --- /dev/null +++ b/src/server/acl/acl_commands_def.h @@ -0,0 +1,93 @@ +// Copyright 2023, DragonflyDB authors. All rights reserved. +// See LICENSE for licensing terms. +// + +#pragma once + +#include "absl/container/flat_hash_map.h" + +namespace dfly::acl { +/* There are 21 ACL categories as of redis 7 + * + * bit 0: keyspace + * bit 1: read + * bit 2: write + * bit 3: set + * bit 4: sortedset + * bit 5: list + * bit 6: hash + * bit 7: string + * bit 8: bitmap + * bit 9: hyperloglog + * bit 10: geo + * bit 11: stream + * bit 12: pubsub + * bit 13: admin + * bit 14: fast + * bit 15: slow + * bit 16: blocking + * bit 17: dangerous + * bit 18: connection + * bit 19: transaction + * bit 20: scripting + * bits 21..28: tba + * Dragonfly extensions: + * bit 29: ft_search + * bit 30: throttle + * bit 31: json + */ + +enum AclCat { + KEYSPACE = 1ULL << 0, + READ = 1ULL << 1, + WRITE = 1ULL << 2, + SET = 1ULL << 3, + SORTEDSET = 1ULL << 4, + LIST = 1ULL << 5, + HASH = 1ULL << 6, + STRING = 1ULL << 7, + BITMAP = 1ULL << 8, + HYPERLOGLOG = 1ULL << 9, + GEO = 1ULL << 10, + STREAM = 1ULL << 11, + PUBSUB = 1ULL << 12, + ADMIN = 1ULL << 13, + FAST = 1ULL << 14, + SLOW = 1ULL << 15, + BLOCKING = 1ULL << 16, + DANGEROUS = 1ULL << 17, + CONNECTION = 1ULL << 18, + TRANSACTION = 1ULL << 19, + SCRIPTING = 1ULL << 20, + FT_SEARCH = 1ULL << 29, + THROTTLE = 1ULL << 30, + JSON = 1ULL << 31 +}; + +// Special flag/mask for all +constexpr uint32_t NONE = 0; +constexpr uint32_t ALL = std::numeric_limits::max(); + +inline const absl::flat_hash_map CATEGORY_INDEX_TABLE{ + {"KEYSPACE", KEYSPACE}, + {"READ", READ}, + {"WRITE", WRITE}, + {"SET", SET}, + {"SORTED_SET", SORTEDSET}, + {"LIST", LIST}, + {"HASH", HASH}, + {"STRING", STRING}, + {"BITMAP", BITMAP}, + {"HYPERLOG", HYPERLOGLOG}, + {"GEO", GEO}, + {"STREAM", STREAM}, + {"PUBSUB", PUBSUB}, + {"ADMIN", ADMIN}, + {"FAST", FAST}, + {"SLOW", SLOW}, + {"BLOCKING", BLOCKING}, + {"DANGEROUS", DANGEROUS}, + {"CONNECTION", CONNECTION}, + {"TRANSACTION", TRANSACTION}, + {"SCRIPTING", SCRIPTING}}; +} // namespace dfly::acl diff --git a/src/server/acl/user.cc b/src/server/acl/user.cc index f8a8257bc..241d53e43 100644 --- a/src/server/acl/user.cc +++ b/src/server/acl/user.cc @@ -6,7 +6,7 @@ #include -namespace dfly { +namespace dfly::acl { namespace { std::string StringSHA256(std::string_view password) { @@ -81,4 +81,4 @@ bool User::IsActive() const { return is_active_; } -} // namespace dfly +} // namespace dfly::acl diff --git a/src/server/acl/user.h b/src/server/acl/user.h index c352212dc..3ec15f933 100644 --- a/src/server/acl/user.h +++ b/src/server/acl/user.h @@ -12,8 +12,9 @@ #include "absl/container/flat_hash_map.h" #include "absl/hash/hash.h" +#include "server/acl/acl_commands_def.h" -namespace dfly { +namespace dfly::acl { class CommandId; @@ -21,87 +22,6 @@ class CommandId; //#bool CheckIfCommandAllowed(uint64_t command_id, const CommandId& command); //#bool CheckIfAclCategoryAllowed(uint64_t command_id, const CommandId& command); -namespace AclCat { -/* There are 21 ACL categories as of redis 7 - * - * bit 0: admin - * bit 1: bitmap - * bit 2: blocking - * bit 3: connection - * bit 4: dangerous - * bit 5: geo - * bit 6: hash - * bit 7: list - * bit 8: set - * bit 9: string - * bit 10: sorttedset - * bit 11: hyperloglog - * bit 12: streams - * bit 13: fast - * bit 14: slow - * bit 15: key-space - * bit 16: pubsub - * bit 17: read - * bit 18: write - * bit 19: scripting - * bit 20: transaction - * - * The rest of the bitfield, will contain special values like @all - * - * bits 21..31: tba - */ -enum AclCat { - ACL_CATEGORY_KEYSPACE = 1ULL << 0, - ACL_CATEGORY_READ = 1ULL << 1, - ACL_CATEGORY_WRITE = 1ULL << 2, - ACL_CATEGORY_SET = 1ULL << 3, - ACL_CATEGORY_SORTEDSET = 1ULL << 4, - ACL_CATEGORY_LIST = 1ULL << 5, - ACL_CATEGORY_HASH = 1ULL << 6, - ACL_CATEGORY_STRING = 1ULL << 7, - ACL_CATEGORY_BITMAP = 1ULL << 8, - ACL_CATEGORY_HYPERLOGLOG = 1ULL << 9, - ACL_CATEGORY_GEO = 1ULL << 10, - ACL_CATEGORY_STREAM = 1ULL << 11, - ACL_CATEGORY_PUBSUB = 1ULL << 12, - ACL_CATEGORY_ADMIN = 1ULL << 13, - ACL_CATEGORY_FAST = 1ULL << 14, - ACL_CATEGORY_SLOW = 1ULL << 15, - ACL_CATEGORY_BLOCKING = 1ULL << 16, - ACL_CATEGORY_DANGEROUS = 1ULL << 17, - ACL_CATEGORY_CONNECTION = 1ULL << 18, - ACL_CATEGORY_TRANSACTION = 1ULL << 19, - ACL_CATEGORY_SCRIPTING = 1ULL << 20 -}; - -// Special flag/mask for all -inline constexpr uint32_t ACL_CATEGORY_NONE = 0; -inline constexpr uint32_t ACL_CATEGORY_ALL = std::numeric_limits::max(); -} // namespace AclCat - -inline const absl::flat_hash_map CATEGORY_INDEX_TABLE{ - {"KEYSPACE", AclCat::ACL_CATEGORY_KEYSPACE}, - {"READ", AclCat::ACL_CATEGORY_READ}, - {"WRITE", AclCat::ACL_CATEGORY_WRITE}, - {"SET", AclCat::ACL_CATEGORY_SET}, - {"SORTED_SET", AclCat::ACL_CATEGORY_SORTEDSET}, - {"LIST", AclCat::ACL_CATEGORY_LIST}, - {"HASH", AclCat::ACL_CATEGORY_HASH}, - {"STRING", AclCat::ACL_CATEGORY_STRING}, - {"BITMAP", AclCat::ACL_CATEGORY_BITMAP}, - {"HYPERLOG", AclCat::ACL_CATEGORY_HYPERLOGLOG}, - {"GEO", AclCat::ACL_CATEGORY_GEO}, - {"STREAM", AclCat::ACL_CATEGORY_STREAM}, - {"PUBSUB", AclCat::ACL_CATEGORY_PUBSUB}, - {"ADMIN", AclCat::ACL_CATEGORY_ADMIN}, - {"FAST", AclCat::ACL_CATEGORY_FAST}, - {"SLOW", AclCat::ACL_CATEGORY_SLOW}, - {"BLOCKING", AclCat::ACL_CATEGORY_BLOCKING}, - {"DANGEROUS", AclCat::ACL_CATEGORY_DANGEROUS}, - {"CONNECTION", AclCat::ACL_CATEGORY_CONNECTION}, - {"TRANSACTION", AclCat::ACL_CATEGORY_TRANSACTION}, - {"SCRIPTING", AclCat::ACL_CATEGORY_SCRIPTING}}; - class User final { public: struct UpdateRequest { @@ -153,7 +73,7 @@ class User final { // when optional is empty, the special `nopass` password is implied // password hashed with xx64 std::optional password_hash_; - uint32_t acl_categories_{AclCat::ACL_CATEGORY_NONE}; + uint32_t acl_categories_{NONE}; // we have at least 221 commands including a bunch of subcommands // LARGE_BITFIELD_DATATYPE acl_commands_; @@ -162,4 +82,4 @@ class User final { bool is_active_{false}; }; -} // namespace dfly +} // namespace dfly::acl diff --git a/src/server/acl/user_registry.cc b/src/server/acl/user_registry.cc index 14384adb4..babc1c8fa 100644 --- a/src/server/acl/user_registry.cc +++ b/src/server/acl/user_registry.cc @@ -8,7 +8,7 @@ #include "core/fibers.h" -namespace dfly { +namespace dfly::acl { void UserRegistry::MaybeAddAndUpdate(std::string_view username, User::UpdateRequest req) { std::unique_lock lock(mu_); @@ -50,4 +50,4 @@ bool UserRegistry::AuthUser(std::string_view username, std::string_view password return user->second.HasPassword(password); } -} // namespace dfly +} // namespace dfly::acl diff --git a/src/server/acl/user_registry.h b/src/server/acl/user_registry.h index 9067b7e1f..b0cf617d5 100644 --- a/src/server/acl/user_registry.h +++ b/src/server/acl/user_registry.h @@ -12,7 +12,7 @@ #include "core/fibers.h" #include "server/acl/user.h" -namespace dfly { +namespace dfly::acl { class UserRegistry { public: @@ -54,4 +54,4 @@ class UserRegistry { mutable util::SharedMutex mu_; }; -} // namespace dfly +} // namespace dfly::acl diff --git a/src/server/acl/user_registry_test.cc b/src/server/acl/user_registry_test.cc index d65f88b07..6dba8f7dc 100644 --- a/src/server/acl/user_registry_test.cc +++ b/src/server/acl/user_registry_test.cc @@ -9,11 +9,12 @@ #include "base/gtest.h" #include "base/logging.h" +#include "server/acl/acl_commands_def.h" #include "server/acl/user.h" using namespace testing; -namespace dfly { +namespace dfly::acl { class UserRegistryTest : public Test {}; @@ -27,19 +28,19 @@ TEST_F(UserRegistryTest, BasicOp) { CHECK_EQ(registry.AuthUser(username, pass), true); CHECK_EQ(registry.IsUserActive(username), false); - CHECK_EQ(registry.GetCredentials(username).acl_categories, AclCat::ACL_CATEGORY_NONE); + CHECK_EQ(registry.GetCredentials(username).acl_categories, NONE); - const uint32_t set_category = 0 | AclCat::ACL_CATEGORY_LIST | AclCat::ACL_CATEGORY_SET; + const uint32_t set_category = NONE | LIST | SET; req = User::UpdateRequest{{}, set_category, {}, {}}; registry.MaybeAddAndUpdate(username, std::move(req)); auto acl_categories = registry.GetCredentials(username).acl_categories; CHECK_EQ(acl_categories, set_category); - req = User::UpdateRequest{{}, {}, 0 | AclCat::ACL_CATEGORY_LIST, {}}; + req = User::UpdateRequest{{}, {}, 0 | LIST, {}}; registry.MaybeAddAndUpdate(username, std::move(req)); acl_categories = registry.GetCredentials(username).acl_categories; - const uint32_t expected_res = 0 | AclCat::ACL_CATEGORY_SET; + const uint32_t expected_res = NONE | SET; CHECK_EQ(acl_categories, expected_res); } -} // namespace dfly +} // namespace dfly::acl diff --git a/src/server/bitops_family.cc b/src/server/bitops_family.cc index c570432d7..9529a14ad 100644 --- a/src/server/bitops_family.cc +++ b/src/server/bitops_family.cc @@ -11,6 +11,7 @@ extern "C" { } #include "base/logging.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/common.h" #include "server/conn_context.h" @@ -828,16 +829,27 @@ OpResult FindFirstBitWithValue(const OpArgs& op_args, std::string_view } // namespace +namespace acl { +constexpr uint32_t kBitPos = READ | BITMAP | SLOW; +constexpr uint32_t kBitCount = READ | BITMAP | SLOW; +constexpr uint32_t kBitField = WRITE | BITMAP | SLOW; +constexpr uint32_t kBitFieldRo = READ | BITMAP | FAST; +constexpr uint32_t kBitOp = WRITE | BITMAP | SLOW; +constexpr uint32_t kGetBit = READ | BITMAP | FAST; +constexpr uint32_t kSetBit = WRITE | BITMAP | SLOW; +} // namespace acl + void BitOpsFamily::Register(CommandRegistry* registry) { using CI = CommandId; - *registry << CI{"BITPOS", CO::CommandOpt::READONLY, -3, 1, 1, 1}.SetHandler(&BitPos) - << CI{"BITCOUNT", CO::READONLY, -2, 1, 1, 1}.SetHandler(&BitCount) - << CI{"BITFIELD", CO::WRITE, -3, 1, 1, 1}.SetHandler(&BitField) - << CI{"BITFIELD_RO", CO::READONLY, -5, 1, 1, 1}.SetHandler(&BitFieldRo) - << CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1}.SetHandler(&BitOp) - << CI{"GETBIT", CO::READONLY | CO::FAST, 3, 1, 1, 1}.SetHandler(&GetBit) - << CI{"SETBIT", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1}.SetHandler(&SetBit); + *registry + << CI{"BITPOS", CO::CommandOpt::READONLY, -3, 1, 1, 1, acl::kBitPos}.SetHandler(&BitPos) + << CI{"BITCOUNT", CO::READONLY, -2, 1, 1, 1, acl::kBitCount}.SetHandler(&BitCount) + << CI{"BITFIELD", CO::WRITE, -3, 1, 1, 1, acl::kBitField}.SetHandler(&BitField) + << CI{"BITFIELD_RO", CO::READONLY, -5, 1, 1, 1, acl::kBitFieldRo}.SetHandler(&BitFieldRo) + << CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1, acl::kBitOp}.SetHandler(&BitOp) + << CI{"GETBIT", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kGetBit}.SetHandler(&GetBit) + << CI{"SETBIT", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1, acl::kSetBit}.SetHandler(&SetBit); } } // namespace dfly diff --git a/src/server/blocking_controller_test.cc b/src/server/blocking_controller_test.cc index 5321950da..072c0259a 100644 --- a/src/server/blocking_controller_test.cc +++ b/src/server/blocking_controller_test.cc @@ -7,6 +7,7 @@ #include #include "base/logging.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/engine_shard_set.h" #include "server/server_state.h" @@ -22,7 +23,7 @@ using namespace testing; class BlockingControllerTest : public Test { protected: - BlockingControllerTest() : cid_("blpop", 0, -3, 1, -2, 1) { + BlockingControllerTest() : cid_("blpop", 0, -3, 1, -2, 1, acl::NONE) { } void SetUp() override; void TearDown() override; diff --git a/src/server/cluster/cluster_family.cc b/src/server/cluster/cluster_family.cc index fd2bc5faa..2d004991a 100644 --- a/src/server/cluster/cluster_family.cc +++ b/src/server/cluster/cluster_family.cc @@ -14,6 +14,7 @@ #include "core/json_object.h" #include "facade/dragonfly_connection.h" #include "facade/error.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/dflycmd.h" @@ -598,12 +599,21 @@ inline CommandId::Handler HandlerFunc(ClusterFamily* se, EngineFunc f) { #define HFUNC(x) SetHandler(HandlerFunc(this, &ClusterFamily::x)) +namespace acl { +constexpr uint32_t kCluster = SLOW; +// Reconsider to maybe more sensible defaults +constexpr uint32_t kDflyCluster = ADMIN | SLOW; +constexpr uint32_t kReadOnly = FAST | CONNECTION; +constexpr uint32_t kReadWrite = FAST | CONNECTION; +} // namespace acl + void ClusterFamily::Register(CommandRegistry* registry) { - *registry << CI{"CLUSTER", CO::READONLY, -2, 0, 0, 0}.HFUNC(Cluster) - << CI{"DFLYCLUSTER", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0}.HFUNC( - DflyCluster) - << CI{"READONLY", CO::READONLY, 1, 0, 0, 0}.HFUNC(ReadOnly) - << CI{"READWRITE", CO::READONLY, 1, 0, 0, 0}.HFUNC(ReadWrite); + *registry << CI{"CLUSTER", CO::READONLY, -2, 0, 0, 0, acl::kCluster}.HFUNC(Cluster) + << CI{"DFLYCLUSTER", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0, + acl::kDflyCluster} + .HFUNC(DflyCluster) + << CI{"READONLY", CO::READONLY, 1, 0, 0, 0, acl::kReadOnly}.HFUNC(ReadOnly) + << CI{"READWRITE", CO::READONLY, 1, 0, 0, 0, acl::kReadWrite}.HFUNC(ReadWrite); } } // namespace dfly diff --git a/src/server/command_registry.cc b/src/server/command_registry.cc index e35408374..9be210d4e 100644 --- a/src/server/command_registry.cc +++ b/src/server/command_registry.cc @@ -31,8 +31,8 @@ using absl::StrCat; using absl::StrSplit; CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, - int8_t last_key, int8_t step) - : facade::CommandId(name, mask, arity, first_key, last_key, step) { + int8_t last_key, int8_t step, uint32_t acl_categories) + : facade::CommandId(name, mask, arity, first_key, last_key, step, acl_categories) { if (mask & CO::ADMIN) opt_mask_ |= CO::NOSCRIPT; diff --git a/src/server/command_registry.h b/src/server/command_registry.h index fe6324c04..9a065c0e6 100644 --- a/src/server/command_registry.h +++ b/src/server/command_registry.h @@ -62,7 +62,7 @@ class CommandId : public facade::CommandId { // NOTICE: name must be a literal string, otherwise metrics break! (see cmd_stats_map in // server_state.h) CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, int8_t last_key, - int8_t step); + int8_t step, uint32_t acl_categories); CommandId(CommandId&&) = default; diff --git a/src/server/generic_family.cc b/src/server/generic_family.cc index 0898158b0..4bfe82706 100644 --- a/src/server/generic_family.cc +++ b/src/server/generic_family.cc @@ -13,6 +13,7 @@ extern "C" { #include "base/flags.h" #include "base/logging.h" #include "redis/rdb.h" +#include "server/acl/acl_commands_def.h" #include "server/blocking_controller.h" #include "server/command_registry.h" #include "server/conn_context.h" @@ -1445,39 +1446,73 @@ using CI = CommandId; #define HFUNC(x) SetHandler(&GenericFamily::x) +namespace acl { +constexpr uint32_t kDel = KEYSPACE | WRITE | SLOW; +constexpr uint32_t kPing = FAST | CONNECTION; +constexpr uint32_t kEcho = FAST | CONNECTION; +constexpr uint32_t kExists = KEYSPACE | READ | FAST; +constexpr uint32_t kTouch = KEYSPACE | READ | FAST; +constexpr uint32_t kExpire = KEYSPACE | WRITE | FAST; +constexpr uint32_t kExpireAt = KEYSPACE | WRITE | FAST; +constexpr uint32_t kPersist = KEYSPACE | WRITE | FAST; +constexpr uint32_t kKeys = KEYSPACE | READ | SLOW | DANGEROUS; +constexpr uint32_t kPExpireAt = KEYSPACE | WRITE | FAST; +constexpr uint32_t kPExpire = KEYSPACE | WRITE | FAST; +constexpr uint32_t kRename = KEYSPACE | WRITE | SLOW; +constexpr uint32_t kRenamNX = KEYSPACE | WRITE | FAST; +constexpr uint32_t kSelect = FAST | CONNECTION; +constexpr uint32_t kScan = KEYSPACE | READ | SLOW; +constexpr uint32_t kTTL = KEYSPACE | READ | FAST; +constexpr uint32_t kPTTL = KEYSPACE | READ | FAST; +constexpr uint32_t kTime = FAST; +constexpr uint32_t kType = KEYSPACE | READ | FAST; +constexpr uint32_t kDump = KEYSPACE | READ | SLOW; +constexpr uint32_t kUnlink = KEYSPACE | WRITE | FAST; +// TODO investigate what stick is +constexpr uint32_t kStick = SLOW; +constexpr uint32_t kSort = WRITE | SET | SORTEDSET | LIST | SLOW | DANGEROUS; +constexpr uint32_t kMove = KEYSPACE | WRITE | FAST; +constexpr uint32_t kRestore = KEYSPACE | WRITE | SLOW | DANGEROUS; +} // namespace acl + void GenericFamily::Register(CommandRegistry* registry) { constexpr auto kSelectOpts = CO::LOADING | CO::FAST | CO::NOSCRIPT; - *registry << CI{"DEL", CO::WRITE, -2, 1, -1, 1}.HFUNC(Del) - /* Redis compatibility: - * We don't allow PING during loading since in Redis PING is used as - * failure detection, and a loading server is considered to be - * not available. */ - << CI{"PING", CO::FAST, -1, 0, 0, 0}.HFUNC(Ping) - << CI{"ECHO", CO::LOADING | CO::FAST, 2, 0, 0, 0}.HFUNC(Echo) - << CI{"EXISTS", CO::READONLY | CO::FAST, -2, 1, -1, 1}.HFUNC(Exists) - << CI{"TOUCH", CO::READONLY | CO::FAST, -2, 1, -1, 1}.HFUNC(Exists) - << CI{"EXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Expire) - << CI{"EXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(ExpireAt) - << CI{"PERSIST", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Persist) - << CI{"KEYS", CO::READONLY, 2, 0, 0, 0}.HFUNC(Keys) - << CI{"PEXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC( - PexpireAt) - << CI{"PEXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Pexpire) - << CI{"RENAME", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1}.HFUNC(Rename) - << CI{"RENAMENX", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1}.HFUNC(RenameNx) - << CI{"SELECT", kSelectOpts, 2, 0, 0, 0}.HFUNC(Select) - << CI{"SCAN", CO::READONLY | CO::FAST | CO::LOADING, -2, 0, 0, 0}.HFUNC(Scan) - << CI{"TTL", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Ttl) - << CI{"PTTL", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Pttl) - << CI{"TIME", CO::LOADING | CO::FAST, 1, 0, 0, 0}.HFUNC(Time) - << CI{"TYPE", CO::READONLY | CO::FAST | CO::LOADING, 2, 1, 1, 1}.HFUNC(Type) - << CI{"DUMP", CO::READONLY, 2, 1, 1, 1}.HFUNC(Dump) - << CI{"UNLINK", CO::WRITE, -2, 1, -1, 1}.HFUNC(Del) - << CI{"STICK", CO::WRITE, -2, 1, -1, 1}.HFUNC(Stick) - << CI{"SORT", CO::READONLY, -2, 1, 1, 1}.HFUNC(Sort) - << CI{"MOVE", CO::WRITE | CO::GLOBAL_TRANS | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Move) - << CI{"RESTORE", CO::WRITE, -4, 1, 1, 1}.HFUNC(Restore); + *registry + << CI{"DEL", CO::WRITE, -2, 1, -1, 1, acl::kDel}.HFUNC(Del) + /* Redis compatibility: + * We don't allow PING during loading since in Redis PING is used as + * failure detection, and a loading server is considered to be + * not available. */ + << CI{"PING", CO::FAST, -1, 0, 0, 0, acl::kPing}.HFUNC(Ping) + << CI{"ECHO", CO::LOADING | CO::FAST, 2, 0, 0, 0, acl::kEcho}.HFUNC(Echo) + << CI{"EXISTS", CO::READONLY | CO::FAST, -2, 1, -1, 1, acl::kExists}.HFUNC(Exists) + << CI{"TOUCH", CO::READONLY | CO::FAST, -2, 1, -1, 1, acl::kTouch}.HFUNC(Exists) + << CI{"EXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kExpire}.HFUNC( + Expire) + << CI{"EXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kExpireAt} + .HFUNC(ExpireAt) + << CI{"PERSIST", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kPersist}.HFUNC(Persist) + << CI{"KEYS", CO::READONLY, 2, 0, 0, 0, acl::kKeys}.HFUNC(Keys) + << CI{"PEXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kPExpireAt} + .HFUNC(PexpireAt) + << CI{"PEXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kPExpire}.HFUNC( + Pexpire) + << CI{"RENAME", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRename}.HFUNC(Rename) + << CI{"RENAMENX", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRenamNX}.HFUNC(RenameNx) + << CI{"SELECT", kSelectOpts, 2, 0, 0, 0, acl::kSelect}.HFUNC(Select) + << CI{"SCAN", CO::READONLY | CO::FAST | CO::LOADING, -2, 0, 0, 0, acl::kScan}.HFUNC(Scan) + << CI{"TTL", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kTTL}.HFUNC(Ttl) + << CI{"PTTL", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kPTTL}.HFUNC(Pttl) + << CI{"TIME", CO::LOADING | CO::FAST, 1, 0, 0, 0, acl::kTime}.HFUNC(Time) + << CI{"TYPE", CO::READONLY | CO::FAST | CO::LOADING, 2, 1, 1, 1, acl::kType}.HFUNC(Type) + << CI{"DUMP", CO::READONLY, 2, 1, 1, 1, acl::kDump}.HFUNC(Dump) + << CI{"UNLINK", CO::WRITE, -2, 1, -1, 1, acl::kUnlink}.HFUNC(Del) + << CI{"STICK", CO::WRITE, -2, 1, -1, 1, acl::kStick}.HFUNC(Stick) + << CI{"SORT", CO::READONLY, -2, 1, 1, 1, acl::kSort}.HFUNC(Sort) + << CI{"MOVE", CO::WRITE | CO::GLOBAL_TRANS | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kMove} + .HFUNC(Move) + << CI{"RESTORE", CO::WRITE, -4, 1, 1, 1, acl::kRestore}.HFUNC(Restore); } } // namespace dfly diff --git a/src/server/hll_family.cc b/src/server/hll_family.cc index eec83e6eb..9384697e0 100644 --- a/src/server/hll_family.cc +++ b/src/server/hll_family.cc @@ -4,6 +4,8 @@ #include "server/hll_family.h" +#include "server/acl/acl_commands_def.h" + extern "C" { #include "redis/hyperloglog.h" } @@ -11,6 +13,7 @@ extern "C" { #include "base/logging.h" #include "base/stl_util.h" #include "facade/error.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/container_utils.h" @@ -283,12 +286,18 @@ void PFMerge(CmdArgList args, ConnectionContext* cntx) { } // namespace +namespace acl { +constexpr uint32_t kPFAdd = WRITE | HYPERLOGLOG | FAST; +constexpr uint32_t kPFCount = READ | HYPERLOGLOG | SLOW; +constexpr uint32_t kPFMerge = WRITE | HYPERLOGLOG | SLOW; +} // namespace acl + void HllFamily::Register(CommandRegistry* registry) { using CI = CommandId; - *registry << CI{"PFADD", CO::WRITE, -3, 1, 1, 1}.SetHandler(PFAdd) - << CI{"PFCOUNT", CO::WRITE, -2, 1, -1, 1}.SetHandler(PFCount) - << CI{"PFMERGE", CO::WRITE, -2, 1, -1, 1}.SetHandler(PFMerge); + *registry << CI{"PFADD", CO::WRITE, -3, 1, 1, 1, acl::kPFAdd}.SetHandler(PFAdd) + << CI{"PFCOUNT", CO::WRITE, -2, 1, -1, 1, acl::kPFCount}.SetHandler(PFCount) + << CI{"PFMERGE", CO::WRITE, -2, 1, -1, 1, acl::kPFMerge}.SetHandler(PFMerge); } const char HllFamily::kInvalidHllErr[] = "Key is not a valid HyperLogLog string value."; diff --git a/src/server/hset_family.cc b/src/server/hset_family.cc index f62ff60a1..d1947865b 100644 --- a/src/server/hset_family.cc +++ b/src/server/hset_family.cc @@ -14,6 +14,7 @@ extern "C" { #include "base/logging.h" #include "core/string_map.h" #include "facade/error.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/container_utils.h" @@ -1060,27 +1061,49 @@ using CI = CommandId; #define HFUNC(x) SetHandler(&HSetFamily::x) -void HSetFamily::Register(CommandRegistry* registry) { - *registry << CI{"HDEL", CO::FAST | CO::WRITE, -3, 1, 1, 1}.HFUNC(HDel) - << CI{"HLEN", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(HLen) - << CI{"HEXISTS", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(HExists) - << CI{"HGET", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(HGet) - << CI{"HGETALL", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(HGetAll) - << CI{"HMGET", CO::FAST | CO::READONLY, -3, 1, 1, 1}.HFUNC(HMGet) - << CI{"HMSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(HSet) - << CI{"HINCRBY", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC(HIncrBy) - << CI{"HINCRBYFLOAT", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC( - HIncrByFloat) - << CI{"HKEYS", CO::READONLY, 2, 1, 1, 1}.HFUNC(HKeys) +namespace acl { +constexpr uint32_t kHDel = WRITE | HASH | FAST; +constexpr uint32_t kHLen = READ | HASH | FAST; +constexpr uint32_t kHExists = READ | HASH | FAST; +constexpr uint32_t kHGet = READ | HASH | FAST; +constexpr uint32_t kHGetAll = READ | HASH | SLOW; +constexpr uint32_t kHMGet = READ | HASH | FAST; +constexpr uint32_t kHMSet = WRITE | HASH | FAST; +constexpr uint32_t kHIncrBy = WRITE | HASH | FAST; +constexpr uint32_t kHIncrByFloat = WRITE | HASH | FAST; +constexpr uint32_t kHKeys = READ | HASH | SLOW; +constexpr uint32_t kHRandField = READ | HASH | SLOW; +constexpr uint32_t kHScan = READ | HASH | SLOW; +constexpr uint32_t kHSet = WRITE | HASH | FAST; +constexpr uint32_t kHSetEx = WRITE | HASH | FAST; +constexpr uint32_t kHSetNx = WRITE | HASH | FAST; +constexpr uint32_t kHStrLen = READ | HASH | FAST; +constexpr uint32_t kHVals = READ | HASH | SLOW; +} // namespace acl - // TODO: add options support - << CI{"HRANDFIELD", CO::READONLY, 2, 1, 1, 1}.HFUNC(HRandField) - << CI{"HSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(HScan) - << CI{"HSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(HSet) - << CI{"HSETEX", CO::WRITE | CO::FAST | CO::DENYOOM, -5, 1, 1, 1}.SetHandler(HSetEx) - << CI{"HSETNX", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC(HSetNx) - << CI{"HSTRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(HStrLen) - << CI{"HVALS", CO::READONLY, 2, 1, 1, 1}.HFUNC(HVals); +void HSetFamily::Register(CommandRegistry* registry) { + *registry + << CI{"HDEL", CO::FAST | CO::WRITE, -3, 1, 1, 1, acl::kHDel}.HFUNC(HDel) + << CI{"HLEN", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kHLen}.HFUNC(HLen) + << CI{"HEXISTS", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kHExists}.HFUNC(HExists) + << CI{"HGET", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kHGet}.HFUNC(HGet) + << CI{"HGETALL", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kHGetAll}.HFUNC(HGetAll) + << CI{"HMGET", CO::FAST | CO::READONLY, -3, 1, 1, 1, acl::kHMGet}.HFUNC(HMGet) + << CI{"HMSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1, acl::kHMSet}.HFUNC(HSet) + << CI{"HINCRBY", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1, acl::kHIncrBy}.HFUNC(HIncrBy) + << CI{"HINCRBYFLOAT", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1, acl::kHIncrByFloat} + .HFUNC(HIncrByFloat) + << CI{"HKEYS", CO::READONLY, 2, 1, 1, 1, acl::kHKeys}.HFUNC(HKeys) + + // TODO: add options support + << CI{"HRANDFIELD", CO::READONLY, 2, 1, 1, 1, acl::kHRandField}.HFUNC(HRandField) + << CI{"HSCAN", CO::READONLY, -3, 1, 1, 1, acl::kHScan}.HFUNC(HScan) + << CI{"HSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1, acl::kHSet}.HFUNC(HSet) + << CI{"HSETEX", CO::WRITE | CO::FAST | CO::DENYOOM, -5, 1, 1, 1, acl::kHSetEx}.SetHandler( + HSetEx) + << CI{"HSETNX", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1, acl::kHSetNx}.HFUNC(HSetNx) + << CI{"HSTRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kHStrLen}.HFUNC(HStrLen) + << CI{"HVALS", CO::READONLY, 2, 1, 1, 1, acl::kHVals}.HFUNC(HVals); } uint32_t HSetFamily::MaxListPackLen() { diff --git a/src/server/json_family.cc b/src/server/json_family.cc index 1c72747aa..d09b39410 100644 --- a/src/server/json_family.cc +++ b/src/server/json_family.cc @@ -19,6 +19,7 @@ extern "C" { #include "base/logging.h" #include "core/json_object.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/error.h" #include "server/journal/journal.h" @@ -1788,33 +1789,42 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) { #define HFUNC(x) SetHandler(&JsonFamily::x) +// Redis modules do not have acl categories, therefore they can not be used by default. +// However, we do not implement those as modules and therefore we can define our own +// sensible defaults. +// For now I introduced only the JSON category which will be the default. +// TODO: Add sensible defaults/categories to json commands + void JsonFamily::Register(CommandRegistry* registry) { - *registry << CI{"JSON.GET", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Get); - *registry << CI{"JSON.MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -3, 1, -2, 1}.HFUNC( - MGet); - *registry << CI{"JSON.TYPE", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(Type); - *registry << CI{"JSON.STRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(StrLen); - *registry << CI{"JSON.OBJLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ObjLen); - *registry << CI{"JSON.ARRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ArrLen); - *registry << CI{"JSON.TOGGLE", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(Toggle); - *registry << CI{"JSON.NUMINCRBY", CO::WRITE | CO::FAST, 4, 1, 1, 1}.HFUNC(NumIncrBy); - *registry << CI{"JSON.NUMMULTBY", CO::WRITE | CO::FAST, 4, 1, 1, 1}.HFUNC(NumMultBy); - *registry << CI{"JSON.DEL", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del); - *registry << CI{"JSON.FORGET", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del); // An alias of JSON.DEL. - *registry << CI{"JSON.OBJKEYS", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ObjKeys); - *registry << CI{"JSON.STRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( - StrAppend); - *registry << CI{"JSON.CLEAR", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(Clear); - *registry << CI{"JSON.ARRPOP", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(ArrPop); - *registry << CI{"JSON.ARRTRIM", CO::WRITE | CO::FAST, 5, 1, 1, 1}.HFUNC(ArrTrim); - *registry << CI{"JSON.ARRINSERT", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( - ArrInsert); - *registry << CI{"JSON.ARRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( - ArrAppend); - *registry << CI{"JSON.ARRINDEX", CO::READONLY | CO::FAST, -4, 1, 1, 1}.HFUNC(ArrIndex); - *registry << CI{"JSON.DEBUG", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Debug); - *registry << CI{"JSON.RESP", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Resp); - *registry << CI{"JSON.SET", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC(Set); + *registry << CI{"JSON.GET", CO::READONLY | CO::FAST, -2, 1, 1, 1, acl::JSON}.HFUNC(Get); + *registry << CI{"JSON.MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -3, 1, -2, 1, + acl::JSON} + .HFUNC(MGet); + *registry << CI{"JSON.TYPE", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Type); + *registry << CI{"JSON.STRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(StrLen); + *registry << CI{"JSON.OBJLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ObjLen); + *registry << CI{"JSON.ARRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ArrLen); + *registry << CI{"JSON.TOGGLE", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Toggle); + *registry << CI{"JSON.NUMINCRBY", CO::WRITE | CO::FAST, 4, 1, 1, 1, acl::JSON}.HFUNC(NumIncrBy); + *registry << CI{"JSON.NUMMULTBY", CO::WRITE | CO::FAST, 4, 1, 1, 1, acl::JSON}.HFUNC(NumMultBy); + *registry << CI{"JSON.DEL", CO::WRITE, -2, 1, 1, 1, acl::JSON}.HFUNC(Del); + *registry << CI{"JSON.FORGET", CO::WRITE, -2, 1, 1, 1, acl::JSON}.HFUNC( + Del); // An alias of JSON.DEL. + *registry << CI{"JSON.OBJKEYS", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ObjKeys); + *registry << CI{"JSON.STRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON} + .HFUNC(StrAppend); + *registry << CI{"JSON.CLEAR", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Clear); + *registry << CI{"JSON.ARRPOP", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::JSON}.HFUNC(ArrPop); + *registry << CI{"JSON.ARRTRIM", CO::WRITE | CO::FAST, 5, 1, 1, 1, acl::JSON}.HFUNC(ArrTrim); + *registry << CI{"JSON.ARRINSERT", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON} + .HFUNC(ArrInsert); + *registry << CI{"JSON.ARRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON} + .HFUNC(ArrAppend); + *registry << CI{"JSON.ARRINDEX", CO::READONLY | CO::FAST, -4, 1, 1, 1, acl::JSON}.HFUNC(ArrIndex); + *registry << CI{"JSON.DEBUG", CO::READONLY | CO::FAST, -2, 1, 1, 1, acl::JSON}.HFUNC(Debug); + *registry << CI{"JSON.RESP", CO::READONLY | CO::FAST, -2, 1, 1, 1, acl::JSON}.HFUNC(Resp); + *registry << CI{"JSON.SET", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON}.HFUNC( + Set); } } // namespace dfly diff --git a/src/server/list_family.cc b/src/server/list_family.cc index 232191e70..86bfe6845 100644 --- a/src/server/list_family.cc +++ b/src/server/list_family.cc @@ -3,6 +3,8 @@ // #include "server/list_family.h" +#include "server/acl/acl_commands_def.h" + extern "C" { #include "redis/object.h" #include "redis/sds.h" @@ -1296,32 +1298,64 @@ using CI = CommandId; #define HFUNC(x) SetHandler(&ListFamily::x) +namespace acl { +constexpr uint32_t kLPush = WRITE | LIST | FAST; +constexpr uint32_t kLPushX = WRITE | LIST | FAST; +constexpr uint32_t kLPop = WRITE | LIST | FAST; +constexpr uint32_t kRPush = WRITE | LIST | FAST; +constexpr uint32_t kRPushX = WRITE | LIST | FAST; +constexpr uint32_t kRPop = WRITE | LIST | FAST; +constexpr uint32_t kRPopLPush = WRITE | LIST | SLOW; +constexpr uint32_t kBRPopLPush = WRITE | LIST | SLOW | BLOCKING; +constexpr uint32_t kBLPop = WRITE | LIST | SLOW | BLOCKING; +constexpr uint32_t kBRPop = WRITE | LIST | SLOW | BLOCKING; +constexpr uint32_t kLLen = READ | LIST | FAST; +constexpr uint32_t kLPos = READ | LIST | SLOW; +constexpr uint32_t kLIndex = READ | LIST | SLOW; +constexpr uint32_t kLInsert = READ | LIST | SLOW; +constexpr uint32_t kLRange = READ | LIST | SLOW; +constexpr uint32_t kLSet = WRITE | LIST | SLOW; +constexpr uint32_t kLTrim = WRITE | LIST | SLOW; +constexpr uint32_t kLRem = WRITE | LIST | SLOW; +constexpr uint32_t kLMove = WRITE | LIST | SLOW; +constexpr uint32_t kBLMove = READ | LIST | SLOW | BLOCKING; +} // namespace acl + void ListFamily::Register(CommandRegistry* registry) { *registry - << CI{"LPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(LPush) - << CI{"LPUSHX", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(LPushX) - << CI{"LPOP", CO::WRITE | CO::FAST, -2, 1, 1, 1}.HFUNC(LPop) - << CI{"RPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(RPush) - << CI{"RPUSHX", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(RPushX) - << CI{"RPOP", CO::WRITE | CO::FAST, -2, 1, 1, 1}.HFUNC(RPop) - << CI{"RPOPLPUSH", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 2, 1}.SetHandler( - RPopLPush) - << CI{"BRPOPLPUSH", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, 4, 1, 2, 1} + << CI{"LPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kLPush}.HFUNC(LPush) + << CI{"LPUSHX", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kLPushX}.HFUNC(LPushX) + << CI{"LPOP", CO::WRITE | CO::FAST, -2, 1, 1, 1, acl::kLPop}.HFUNC(LPop) + << CI{"RPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kRPush}.HFUNC(RPush) + << CI{"RPUSHX", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kRPushX}.HFUNC(RPushX) + << CI{"RPOP", CO::WRITE | CO::FAST, -2, 1, 1, 1, acl::kRPop}.HFUNC(RPop) + << CI{"RPOPLPUSH", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRPopLPush} + .SetHandler(RPopLPush) + << CI{"BRPOPLPUSH", + CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, + 4, + 1, + 2, + 1, + acl::kBRPopLPush} .SetHandler(BRPopLPush) - << CI{"BLPOP", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1} + << CI{"BLPOP", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1, + acl::kBLPop} .HFUNC(BLPop) - << CI{"BRPOP", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1} + << CI{"BRPOP", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1, + acl::kBRPop} .HFUNC(BRPop) - << CI{"LLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(LLen) - << CI{"LPOS", CO::READONLY | CO::FAST, -3, 1, 1, 1}.HFUNC(LPos) - << CI{"LINDEX", CO::READONLY, 3, 1, 1, 1}.HFUNC(LIndex) - << CI{"LINSERT", CO::WRITE | CO::DENYOOM, 5, 1, 1, 1}.HFUNC(LInsert) - << CI{"LRANGE", CO::READONLY, 4, 1, 1, 1}.HFUNC(LRange) - << CI{"LSET", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1}.HFUNC(LSet) - << CI{"LTRIM", CO::WRITE, 4, 1, 1, 1}.HFUNC(LTrim) - << CI{"LREM", CO::WRITE, 4, 1, 1, 1}.HFUNC(LRem) - << CI{"LMOVE", CO::WRITE | CO::NO_AUTOJOURNAL, 5, 1, 2, 1}.HFUNC(LMove) - << CI{"BLMOVE", CO::WRITE | CO::NO_AUTOJOURNAL | CO::BLOCKING, 6, 1, 2, 1}.SetHandler(BLMove); + << CI{"LLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kLLen}.HFUNC(LLen) + << CI{"LPOS", CO::READONLY | CO::FAST, -3, 1, 1, 1, acl::kLPos}.HFUNC(LPos) + << CI{"LINDEX", CO::READONLY, 3, 1, 1, 1, acl::kLIndex}.HFUNC(LIndex) + << CI{"LINSERT", CO::WRITE | CO::DENYOOM, 5, 1, 1, 1, acl::kLInsert}.HFUNC(LInsert) + << CI{"LRANGE", CO::READONLY, 4, 1, 1, 1, acl::kLRange}.HFUNC(LRange) + << CI{"LSET", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1, acl::kLSet}.HFUNC(LSet) + << CI{"LTRIM", CO::WRITE, 4, 1, 1, 1, acl::kLTrim}.HFUNC(LTrim) + << CI{"LREM", CO::WRITE, 4, 1, 1, 1, acl::kLRem}.HFUNC(LRem) + << CI{"LMOVE", CO::WRITE | CO::NO_AUTOJOURNAL, 5, 1, 2, 1, acl::kLMove}.HFUNC(LMove) + << CI{"BLMOVE", CO::WRITE | CO::NO_AUTOJOURNAL | CO::BLOCKING, 6, 1, 2, 1, acl::kBLMove} + .SetHandler(BLMove); } } // namespace dfly diff --git a/src/server/main_service.cc b/src/server/main_service.cc index 4707febaa..47ec3a590 100644 --- a/src/server/main_service.cc +++ b/src/server/main_service.cc @@ -22,6 +22,7 @@ extern "C" { #include "facade/dragonfly_connection.h" #include "facade/error.h" #include "facade/reply_capture.h" +#include "server/acl/acl_commands_def.h" #include "server/bitops_family.h" #include "server/cluster/cluster_family.h" #include "server/conn_context.h" @@ -2019,29 +2020,55 @@ using ServiceFunc = void (Service::*)(CmdArgList, ConnectionContext* cntx); #define MFUNC(x) \ SetHandler([this](CmdArgList sp, ConnectionContext* cntx) { this->x(std::move(sp), cntx); }) +namespace acl { +constexpr uint32_t kQuit = FAST | CONNECTION; +constexpr uint32_t kMulti = FAST | TRANSACTION; +constexpr uint32_t kWatch = FAST | TRANSACTION; +constexpr uint32_t kUnwatch = FAST | TRANSACTION; +constexpr uint32_t kDiscard = FAST | TRANSACTION; +constexpr uint32_t kEval = SLOW | SCRIPTING; +constexpr uint32_t kEvalSha = SLOW | SCRIPTING; +constexpr uint32_t kExec = SLOW | TRANSACTION; +constexpr uint32_t kPublish = PUBSUB | FAST; +constexpr uint32_t kSubscribe = PUBSUB | SLOW; +constexpr uint32_t kUnsubscribe = PUBSUB | SLOW; +constexpr uint32_t kPSubscribe = PUBSUB | SLOW; +constexpr uint32_t kPUnsubsribe = PUBSUB | SLOW; +constexpr uint32_t kFunction = SLOW; +constexpr uint32_t kMonitor = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kPubSub = SLOW; +constexpr uint32_t kCommand = SLOW | CONNECTION; +} // namespace acl + void Service::RegisterCommands() { using CI = CommandId; registry_ - << CI{"QUIT", CO::READONLY | CO::FAST, 1, 0, 0, 0}.HFUNC(Quit) - << CI{"MULTI", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0}.HFUNC(Multi) - << CI{"WATCH", CO::LOADING, -2, 1, -1, 1}.HFUNC(Watch) - << CI{"UNWATCH", CO::LOADING, 1, 0, 0, 0}.HFUNC(Unwatch) - << CI{"DISCARD", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0}.MFUNC(Discard) - << CI{"EVAL", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1}.MFUNC(Eval).SetValidator( - &EvalValidator) - << CI{"EVALSHA", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1}.MFUNC(EvalSha).SetValidator( - &EvalValidator) - << CI{"EXEC", CO::LOADING | CO::NOSCRIPT, 1, 0, 0, 1}.MFUNC(Exec) - << CI{"PUBLISH", CO::LOADING | CO::FAST, 3, 0, 0, 0}.MFUNC(Publish) - << CI{"SUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(Subscribe) - << CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.MFUNC(Unsubscribe) - << CI{"PSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(PSubscribe) - << CI{"PUNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.MFUNC(PUnsubscribe) - << CI{"FUNCTION", CO::NOSCRIPT, 2, 0, 0, 0}.MFUNC(Function) - << CI{"MONITOR", CO::ADMIN, 1, 0, 0, 0}.MFUNC(Monitor) - << CI{"PUBSUB", CO::LOADING | CO::FAST, -1, 0, 0, 0}.MFUNC(Pubsub) - << CI{"COMMAND", CO::LOADING | CO::NOSCRIPT, -1, 0, 0, 0}.MFUNC(Command); + << CI{"QUIT", CO::READONLY | CO::FAST, 1, 0, 0, 0, acl::kQuit}.HFUNC(Quit) + << CI{"MULTI", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kMulti}.HFUNC(Multi) + << CI{"WATCH", CO::LOADING, -2, 1, -1, 1, acl::kWatch}.HFUNC(Watch) + << CI{"UNWATCH", CO::LOADING, 1, 0, 0, 0, acl::kUnwatch}.HFUNC(Unwatch) + << CI{"DISCARD", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kDiscard}.MFUNC( + Discard) + << CI{"EVAL", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1, acl::kEval} + .MFUNC(Eval) + .SetValidator(&EvalValidator) + << CI{"EVALSHA", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1, acl::kEvalSha} + .MFUNC(EvalSha) + .SetValidator(&EvalValidator) + << CI{"EXEC", CO::LOADING | CO::NOSCRIPT, 1, 0, 0, 1, acl::kExec}.MFUNC(Exec) + << CI{"PUBLISH", CO::LOADING | CO::FAST, 3, 0, 0, 0, acl::kPublish}.MFUNC(Publish) + << CI{"SUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kSubscribe}.MFUNC(Subscribe) + << CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0, acl::kUnsubscribe}.MFUNC( + Unsubscribe) + << CI{"PSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kPSubscribe}.MFUNC( + PSubscribe) + << CI{"PUNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0, acl::kPUnsubsribe}.MFUNC( + PUnsubscribe) + << CI{"FUNCTION", CO::NOSCRIPT, 2, 0, 0, 0, acl::kFunction}.MFUNC(Function) + << CI{"MONITOR", CO::ADMIN, 1, 0, 0, 0, acl::kMonitor}.MFUNC(Monitor) + << CI{"PUBSUB", CO::LOADING | CO::FAST, -1, 0, 0, 0, acl::kPubSub}.MFUNC(Pubsub) + << CI{"COMMAND", CO::LOADING | CO::NOSCRIPT, -1, 0, 0, 0, acl::kCommand}.MFUNC(Command); StreamFamily::Register(®istry_); StringFamily::Register(®istry_); diff --git a/src/server/search/search_family.cc b/src/server/search/search_family.cc index 9f6c812be..590cf3f13 100644 --- a/src/server/search/search_family.cc +++ b/src/server/search/search_family.cc @@ -18,6 +18,7 @@ #include "core/search/search.h" #include "facade/error.h" #include "facade/reply_builder.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/container_utils.h" @@ -373,15 +374,19 @@ void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) { #define HFUNC(x) SetHandler(&SearchFamily::x) +// Redis search is a module. Therefore we introduce dragonfly extension search +// to set as the default for the search family of commands. More sensible defaults, +// should also be considered in the future + void SearchFamily::Register(CommandRegistry* registry) { using CI = CommandId; - *registry << CI{"FT.CREATE", CO::GLOBAL_TRANS, -2, 0, 0, 0}.HFUNC(FtCreate) - << CI{"FT.DROPINDEX", CO::GLOBAL_TRANS, -2, 0, 0, 0}.HFUNC(FtDropIndex) - << CI{"FT.INFO", CO::GLOBAL_TRANS, 2, 0, 0, 0}.HFUNC(FtInfo) + *registry << CI{"FT.CREATE", CO::GLOBAL_TRANS, -2, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtCreate) + << CI{"FT.DROPINDEX", CO::GLOBAL_TRANS, -2, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtDropIndex) + << CI{"FT.INFO", CO::GLOBAL_TRANS, 2, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtInfo) // Underscore same as in RediSearch because it's "temporary" (long time already) - << CI{"FT._LIST", CO::GLOBAL_TRANS, 1, 0, 0, 0}.HFUNC(FtList) - << CI{"FT.SEARCH", CO::GLOBAL_TRANS, -3, 0, 0, 0}.HFUNC(FtSearch); + << CI{"FT._LIST", CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtList) + << CI{"FT.SEARCH", CO::GLOBAL_TRANS, -3, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtSearch); } } // namespace dfly diff --git a/src/server/server_family.cc b/src/server/server_family.cc index 267202165..483302696 100644 --- a/src/server/server_family.cc +++ b/src/server/server_family.cc @@ -28,6 +28,7 @@ extern "C" { #include "facade/reply_builder.h" #include "io/file_util.h" #include "io/proc_reader.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/debugcmd.h" @@ -2414,34 +2415,63 @@ void ServerFamily::Dfly(CmdArgList args, ConnectionContext* cntx) { #define HFUNC(x) SetHandler(HandlerFunc(this, &ServerFamily::x)) +namespace acl { +constexpr uint32_t kAuth = FAST | CONNECTION; +constexpr uint32_t kBGSave = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kClient = SLOW | CONNECTION; +constexpr uint32_t kConfig = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kDbSize = KEYSPACE | READ | FAST; +constexpr uint32_t kDebug = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kFlushDB = KEYSPACE | WRITE | SLOW | DANGEROUS; +constexpr uint32_t kFlushAll = KEYSPACE | WRITE | SLOW | DANGEROUS; +constexpr uint32_t kInfo = SLOW | DANGEROUS; +constexpr uint32_t kHello = FAST | CONNECTION; +constexpr uint32_t kLastSave = ADMIN | FAST | DANGEROUS; +constexpr uint32_t kLatency = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kMemory = READ | SLOW; +constexpr uint32_t kSave = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kShutDown = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kSlaveOf = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kReplicaOf = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kReplTakeOver = DANGEROUS; +constexpr uint32_t kReplConf = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kRole = ADMIN | FAST | DANGEROUS; +constexpr uint32_t kSlowLog = ADMIN | SLOW | DANGEROUS; +constexpr uint32_t kScript = SLOW | SCRIPTING; +constexpr uint32_t kDfly = ADMIN; +} // namespace acl + void ServerFamily::Register(CommandRegistry* registry) { constexpr auto kReplicaOpts = CO::LOADING | CO::ADMIN | CO::GLOBAL_TRANS; constexpr auto kMemOpts = CO::LOADING | CO::READONLY | CO::FAST | CO::NOSCRIPT; - *registry << CI{"AUTH", CO::NOSCRIPT | CO::FAST | CO::LOADING, -2, 0, 0, 0}.HFUNC(Auth) - << CI{"BGSAVE", CO::ADMIN | CO::GLOBAL_TRANS, 1, 0, 0, 0}.HFUNC(Save) - << CI{"CLIENT", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.HFUNC(Client) - << CI{"CONFIG", CO::ADMIN, -2, 0, 0, 0}.HFUNC(Config) - << CI{"DBSIZE", CO::READONLY | CO::FAST | CO::LOADING, 1, 0, 0, 0}.HFUNC(DbSize) - << CI{"DEBUG", CO::ADMIN | CO::LOADING, -2, 0, 0, 0}.HFUNC(Debug) - << CI{"FLUSHDB", CO::WRITE | CO::GLOBAL_TRANS, 1, 0, 0, 0}.HFUNC(FlushDb) - << CI{"FLUSHALL", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0}.HFUNC(FlushAll) - << CI{"INFO", CO::LOADING, -1, 0, 0, 0}.HFUNC(Info) - << CI{"HELLO", CO::LOADING, -1, 0, 0, 0}.HFUNC(Hello) - << CI{"LASTSAVE", CO::LOADING | CO::FAST, 1, 0, 0, 0}.HFUNC(LastSave) - << CI{"LATENCY", CO::NOSCRIPT | CO::LOADING | CO::FAST, -2, 0, 0, 0}.HFUNC(Latency) - << CI{"MEMORY", kMemOpts, -2, 0, 0, 0}.HFUNC(Memory) - << CI{"SAVE", CO::ADMIN | CO::GLOBAL_TRANS, -1, 0, 0, 0}.HFUNC(Save) - << CI{"SHUTDOWN", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.HFUNC( - ShutdownCmd) - << CI{"SLAVEOF", kReplicaOpts, 3, 0, 0, 0}.HFUNC(ReplicaOf) - << CI{"REPLICAOF", kReplicaOpts, 3, 0, 0, 0}.HFUNC(ReplicaOf) - << CI{"REPLTAKEOVER", CO::ADMIN | CO::GLOBAL_TRANS, 2, 0, 0, 0}.HFUNC(ReplTakeOver) - << CI{"REPLCONF", CO::ADMIN | CO::LOADING, -1, 0, 0, 0}.HFUNC(ReplConf) - << CI{"ROLE", CO::LOADING | CO::FAST | CO::NOSCRIPT, 1, 0, 0, 0}.HFUNC(Role) - << CI{"SLOWLOG", CO::ADMIN | CO::FAST, -2, 0, 0, 0}.SetHandler(SlowLog) - << CI{"SCRIPT", CO::NOSCRIPT | CO::NO_KEY_JOURNAL, -2, 0, 0, 0}.HFUNC(Script) - << CI{"DFLY", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0}.HFUNC(Dfly); + *registry + << CI{"AUTH", CO::NOSCRIPT | CO::FAST | CO::LOADING, -2, 0, 0, 0, acl::kAuth}.HFUNC(Auth) + << CI{"BGSAVE", CO::ADMIN | CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::kBGSave}.HFUNC(Save) + << CI{"CLIENT", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kClient}.HFUNC(Client) + << CI{"CONFIG", CO::ADMIN, -2, 0, 0, 0, acl::kConfig}.HFUNC(Config) + << CI{"DBSIZE", CO::READONLY | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kDbSize}.HFUNC(DbSize) + << CI{"DEBUG", CO::ADMIN | CO::LOADING, -2, 0, 0, 0, acl::kDebug}.HFUNC(Debug) + << CI{"FLUSHDB", CO::WRITE | CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::kFlushDB}.HFUNC(FlushDb) + << CI{"FLUSHALL", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0, acl::kFlushAll}.HFUNC(FlushAll) + << CI{"INFO", CO::LOADING, -1, 0, 0, 0, acl::kInfo}.HFUNC(Info) + << CI{"HELLO", CO::LOADING, -1, 0, 0, 0, acl::kHello}.HFUNC(Hello) + << CI{"LASTSAVE", CO::LOADING | CO::FAST, 1, 0, 0, 0, acl::kLastSave}.HFUNC(LastSave) + << CI{"LATENCY", CO::NOSCRIPT | CO::LOADING | CO::FAST, -2, 0, 0, 0, acl::kLatency}.HFUNC( + Latency) + << CI{"MEMORY", kMemOpts, -2, 0, 0, 0, acl::kMemory}.HFUNC(Memory) + << CI{"SAVE", CO::ADMIN | CO::GLOBAL_TRANS, -1, 0, 0, 0, acl::kSave}.HFUNC(Save) + << CI{"SHUTDOWN", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0, acl::kShutDown}.HFUNC( + ShutdownCmd) + << CI{"SLAVEOF", kReplicaOpts, 3, 0, 0, 0, acl::kSlaveOf}.HFUNC(ReplicaOf) + << CI{"REPLICAOF", kReplicaOpts, 3, 0, 0, 0, acl::kReplicaOf}.HFUNC(ReplicaOf) + << CI{"REPLTAKEOVER", CO::ADMIN | CO::GLOBAL_TRANS, 2, 0, 0, 0, acl::kReplTakeOver}.HFUNC( + ReplTakeOver) + << CI{"REPLCONF", CO::ADMIN | CO::LOADING, -1, 0, 0, 0, acl::kReplConf}.HFUNC(ReplConf) + << CI{"ROLE", CO::LOADING | CO::FAST | CO::NOSCRIPT, 1, 0, 0, 0, acl::kRole}.HFUNC(Role) + << CI{"SLOWLOG", CO::ADMIN | CO::FAST, -2, 0, 0, 0, acl::kSlowLog}.SetHandler(SlowLog) + << CI{"SCRIPT", CO::NOSCRIPT | CO::NO_KEY_JOURNAL, -2, 0, 0, 0, acl::kScript}.HFUNC(Script) + << CI{"DFLY", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0, acl::kDfly}.HFUNC(Dfly); } } // namespace dfly diff --git a/src/server/set_family.cc b/src/server/set_family.cc index 161a1af76..342f550c5 100644 --- a/src/server/set_family.cc +++ b/src/server/set_family.cc @@ -15,6 +15,7 @@ extern "C" { #include "base/logging.h" #include "base/stl_util.h" #include "core/string_set.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/container_utils.h" @@ -1560,28 +1561,52 @@ using CI = CommandId; #define HFUNC(x) SetHandler(&x) +namespace acl { +constexpr uint32_t kSAdd = WRITE | SET | FAST; +constexpr uint32_t kSDiff = READ | SET | SLOW; +constexpr uint32_t kSDiffStore = WRITE | SET | SLOW; +constexpr uint32_t kSInter = READ | SET | SLOW; +constexpr uint32_t kSInterStore = WRITE | SET | SLOW; +constexpr uint32_t kSMembers = READ | SET | SLOW; +constexpr uint32_t kSIsMember = READ | SET | SLOW; +constexpr uint32_t kSMIsMember = READ | SET | FAST; +constexpr uint32_t kSMove = WRITE | SET | FAST; +constexpr uint32_t kSRem = WRITE | SET | FAST; +constexpr uint32_t kSCard = READ | SET | FAST; +constexpr uint32_t kSPop = WRITE | SET | SLOW; +constexpr uint32_t kSUnion = READ | SET | SLOW; +constexpr uint32_t kSUnionStore = WRITE | SET | SLOW; +constexpr uint32_t kSScan = READ | SET | SLOW; +} // namespace acl + void SetFamily::Register(CommandRegistry* registry) { - *registry << CI{"SADD", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(SAdd) - << CI{"SDIFF", CO::READONLY, -2, 1, -1, 1}.HFUNC(SDiff) - << CI{"SDIFFSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( - SDiffStore) - << CI{"SINTER", CO::READONLY, -2, 1, -1, 1}.HFUNC(SInter) - << CI{"SINTERSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( - SInterStore) - << CI{"SMEMBERS", CO::READONLY, 2, 1, 1, 1}.HFUNC(SMembers) - << CI{"SISMEMBER", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(SIsMember) - << CI{"SMISMEMBER", CO::READONLY, -3, 1, 1, 1}.HFUNC(SMIsMember) - << CI{"SMOVE", CO::FAST | CO::WRITE | CO::NO_AUTOJOURNAL, 4, 1, 2, 1}.HFUNC(SMove) - << CI{"SREM", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(SRem) - << CI{"SCARD", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(SCard) - << CI{"SPOP", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, -2, 1, 1, 1}.HFUNC(SPop) - << CI{"SUNION", CO::READONLY, -2, 1, -1, 1}.HFUNC(SUnion) - << CI{"SUNIONSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( - SUnionStore) - << CI{"SSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(SScan); + *registry + << CI{"SADD", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kSAdd}.HFUNC(SAdd) + << CI{"SDIFF", CO::READONLY, -2, 1, -1, 1, acl::kSDiff}.HFUNC(SDiff) + << CI{"SDIFFSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1, + acl::kSDiffStore} + .HFUNC(SDiffStore) + << CI{"SINTER", CO::READONLY, -2, 1, -1, 1, acl::kSInter}.HFUNC(SInter) + << CI{"SINTERSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1, + acl::kSInterStore} + .HFUNC(SInterStore) + << CI{"SMEMBERS", CO::READONLY, 2, 1, 1, 1, acl::kSMembers}.HFUNC(SMembers) + << CI{"SISMEMBER", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kSIsMember}.HFUNC(SIsMember) + << CI{"SMISMEMBER", CO::READONLY, -3, 1, 1, 1, acl::kSMIsMember}.HFUNC(SMIsMember) + << CI{"SMOVE", CO::FAST | CO::WRITE | CO::NO_AUTOJOURNAL, 4, 1, 2, 1, acl::kSMove}.HFUNC( + SMove) + << CI{"SREM", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::kSRem}.HFUNC(SRem) + << CI{"SCARD", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kSCard}.HFUNC(SCard) + << CI{"SPOP", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, -2, 1, 1, 1, acl::kSPop}.HFUNC(SPop) + << CI{"SUNION", CO::READONLY, -2, 1, -1, 1, acl::kSUnion}.HFUNC(SUnion) + << CI{"SUNIONSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1, + acl::kSUnionStore} + .HFUNC(SUnionStore) + << CI{"SSCAN", CO::READONLY, -3, 1, 1, 1, acl::kSScan}.HFUNC(SScan); if (absl::GetFlag(FLAGS_use_set2)) { - *registry << CI{"SADDEX", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(SAddEx); + *registry << CI{"SADDEX", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1, acl::kSAdd}.HFUNC( + SAddEx); } } diff --git a/src/server/stream_family.cc b/src/server/stream_family.cc index c15b1d5e6..3bcfef58c 100644 --- a/src/server/stream_family.cc +++ b/src/server/stream_family.cc @@ -13,6 +13,7 @@ extern "C" { #include "base/logging.h" #include "facade/error.h" +#include "server/acl/acl_commands_def.h" #include "server/blocking_controller.h" #include "server/command_registry.h" #include "server/conn_context.h" @@ -2005,23 +2006,42 @@ void StreamFamily::XRangeGeneric(CmdArgList args, bool is_rev, ConnectionContext #define HFUNC(x) SetHandler(&StreamFamily::x) +namespace acl { +constexpr uint32_t kXAdd = WRITE | STREAM | FAST; +constexpr uint32_t kXDel = WRITE | STREAM | FAST; +constexpr uint32_t kXGroup = SLOW; +constexpr uint32_t kXInfo = SLOW; +constexpr uint32_t kXLen = READ | STREAM | FAST; +constexpr uint32_t kXRange = READ | STREAM | SLOW; +constexpr uint32_t kXRevRange = READ | STREAM | SLOW; +constexpr uint32_t kXRead = READ | STREAM | SLOW | BLOCKING; +constexpr uint32_t kXReadGroup = WRITE | STREAM | SLOW | BLOCKING; +constexpr uint32_t kXSetId = WRITE | STREAM | SLOW; +constexpr uint32_t kXTrim = WRITE | STREAM | SLOW; +constexpr uint32_t kXGroupHelp = READ | STREAM | SLOW; +} // namespace acl + void StreamFamily::Register(CommandRegistry* registry) { using CI = CommandId; - *registry << CI{"XADD", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1}.HFUNC(XAdd) - << CI{"XDEL", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(XDel) - << CI{"XGROUP", CO::WRITE | CO::DENYOOM, -3, 2, 2, 1}.HFUNC(XGroup) - << CI{"XINFO", CO::READONLY | CO::NOSCRIPT, -2, 0, 0, 0}.HFUNC(XInfo) - << CI{"XLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(XLen) - << CI{"XRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(XRange) - << CI{"XREVRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(XRevRange) - << CI{"XREAD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 3, 3, 1} - .HFUNC(XRead) - << CI{"XREADGROUP", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -6, 6, 6, 1} - .HFUNC(XReadGroup) - << CI{"XSETID", CO::WRITE, 3, 1, 1, 1}.HFUNC(XSetId) - << CI{"XTRIM", CO::WRITE | CO::FAST, -4, 1, 1, 1}.HFUNC(XTrim) - << CI{"_XGROUP_HELP", CO::NOSCRIPT | CO::HIDDEN, 2, 0, 0, 0}.SetHandler(XGroupHelp); + *registry + << CI{"XADD", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1, acl::kXAdd}.HFUNC(XAdd) + << CI{"XDEL", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::kXDel}.HFUNC(XDel) + << CI{"XGROUP", CO::WRITE | CO::DENYOOM, -3, 2, 2, 1, acl::kXGroup}.HFUNC(XGroup) + << CI{"XINFO", CO::READONLY | CO::NOSCRIPT, -2, 0, 0, 0, acl::kXInfo}.HFUNC(XInfo) + << CI{"XLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kXLen}.HFUNC(XLen) + << CI{"XRANGE", CO::READONLY, -4, 1, 1, 1, acl::kXRange}.HFUNC(XRange) + << CI{"XREVRANGE", CO::READONLY, -4, 1, 1, 1, acl::kXRevRange}.HFUNC(XRevRange) + << CI{"XREAD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 3, 3, 1, + acl::kXRead} + .HFUNC(XRead) + << CI{"XREADGROUP", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -6, 6, 6, 1, + acl::kXReadGroup} + .HFUNC(XReadGroup) + << CI{"XSETID", CO::WRITE, 3, 1, 1, 1, acl::kXSetId}.HFUNC(XSetId) + << CI{"XTRIM", CO::WRITE | CO::FAST, -4, 1, 1, 1, acl::kXTrim}.HFUNC(XTrim) + << CI{"_XGROUP_HELP", CO::NOSCRIPT | CO::HIDDEN, 2, 0, 0, 0, acl::kXGroupHelp}.SetHandler( + XGroupHelp); } } // namespace dfly diff --git a/src/server/string_family.cc b/src/server/string_family.cc index 1b498bf21..344cd9ba3 100644 --- a/src/server/string_family.cc +++ b/src/server/string_family.cc @@ -19,6 +19,7 @@ extern "C" { #include "base/logging.h" #include "redis/util.h" +#include "server/acl/acl_commands_def.h" #include "server/command_registry.h" #include "server/conn_context.h" #include "server/engine_shard_set.h" @@ -1467,32 +1468,67 @@ void StringFamily::Shutdown() { #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) { *registry - << CI{"SET", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, 1, 1}.HFUNC(Set) - << CI{"SETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1}.HFUNC(SetEx) - << CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1}.HFUNC(PSetEx) - << CI{"SETNX", CO::WRITE | CO::DENYOOM, 3, 1, 1, 1}.HFUNC(SetNx) - << CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(Append) - << CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(Prepend) - << CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Incr) - << CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Decr) - << CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(IncrBy) - << CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(IncrByFloat) - << CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(DecrBy) - << CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Get) - << CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(GetDel) - << CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -1, 1, 1, 1}.HFUNC( - GetEx) - << CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(GetSet) - << CI{"MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -2, 1, -1, 1}.HFUNC(MGet) - << CI{"MSET", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2}.HFUNC(MSet) - << CI{"MSETNX", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2}.HFUNC(MSetNx) - << CI{"STRLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(StrLen) - << CI{"GETRANGE", CO::READONLY | CO::FAST, 4, 1, 1, 1}.HFUNC(GetRange) - << CI{"SUBSTR", CO::READONLY | CO::FAST, 4, 1, 1, 1}.HFUNC(GetRange) // Alias for GetRange - << CI{"SETRANGE", CO::WRITE | CO::FAST | CO::DENYOOM, 4, 1, 1, 1}.HFUNC(SetRange) - << CI{"CL.THROTTLE", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1}.HFUNC(ClThrottle); + << CI{"SET", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, 1, 1, acl::kSet}.HFUNC(Set) + << CI{"SETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1, acl::kSetEx}.HFUNC( + SetEx) + << CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1, acl::kPSetEx}.HFUNC( + PSetEx) + << CI{"SETNX", CO::WRITE | CO::DENYOOM, 3, 1, 1, 1, acl::kSetNx}.HFUNC(SetNx) + << CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kAppend}.HFUNC(Append) + << CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kPrepend}.HFUNC(Prepend) + << CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kIncr}.HFUNC(Incr) + << CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kDecr}.HFUNC(Decr) + << CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kIncrBy}.HFUNC(IncrBy) + << CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kIncrByFloat}.HFUNC(IncrByFloat) + << CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kDecrBy}.HFUNC(DecrBy) + << CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kGet}.HFUNC(Get) + << CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kGetDel}.HFUNC(GetDel) + << CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -1, 1, 1, 1, + acl::kGetEx} + .HFUNC(GetEx) + << CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kGetSet}.HFUNC(GetSet) + << CI{"MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -2, 1, -1, 1, acl::kMGet}.HFUNC( + MGet) + << CI{"MSET", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2, acl::kMSet}.HFUNC(MSet) + << CI{"MSETNX", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2, acl::kMSetNx}.HFUNC(MSetNx) + << CI{"STRLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kStrLen}.HFUNC(StrLen) + << CI{"GETRANGE", CO::READONLY | CO::FAST, 4, 1, 1, 1, acl::kGetRange}.HFUNC(GetRange) + << CI{"SUBSTR", CO::READONLY | CO::FAST, 4, 1, 1, 1, acl::kSubStr}.HFUNC( + GetRange) // Alias for GetRange + << CI{"SETRANGE", CO::WRITE | CO::FAST | CO::DENYOOM, 4, 1, 1, 1, acl::kSetRange}.HFUNC( + SetRange) + << CI{"CL.THROTTLE", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1, acl::kClThrottle}.HFUNC( + ClThrottle); } } // namespace dfly diff --git a/src/server/test_utils.cc b/src/server/test_utils.cc index 9ebdafc0b..b87ffcab3 100644 --- a/src/server/test_utils.cc +++ b/src/server/test_utils.cc @@ -4,6 +4,8 @@ #include "server/test_utils.h" +#include "server/acl/acl_commands_def.h" + extern "C" { #include "redis/zmalloc.h" } @@ -67,7 +69,7 @@ void TestConnection::SendPubMessageAsync(PubMessage pmsg) { } void TransactionSuspension::Start() { - CommandId cid{"TEST", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0}; + CommandId cid{"TEST", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0, acl::NONE}; transaction_ = new dfly::Transaction{&cid}; diff --git a/src/server/zset_family.cc b/src/server/zset_family.cc index fa0650bd0..1c2cf1b81 100644 --- a/src/server/zset_family.cc +++ b/src/server/zset_family.cc @@ -4,6 +4,8 @@ #include "server/zset_family.h" +#include "server/acl/acl_commands_def.h" + extern "C" { #include "redis/geohash.h" #include "redis/geohash_helper.h" @@ -2534,49 +2536,101 @@ void ZSetFamily::GeoDist(CmdArgList args, ConnectionContext* cntx) { #define HFUNC(x) SetHandler(&ZSetFamily::x) +namespace acl { +constexpr uint32_t kZAdd = WRITE | SORTEDSET | FAST; +constexpr uint32_t kBZPopMin = WRITE | SORTEDSET | FAST | BLOCKING; +constexpr uint32_t kBZPopMax = WRITE | SORTEDSET | FAST | BLOCKING; +constexpr uint32_t kZCard = READ | SORTEDSET | FAST; +constexpr uint32_t kZCount = READ | SORTEDSET | FAST; +constexpr uint32_t kZDiff = READ | SORTEDSET | SLOW; +constexpr uint32_t kZIncrBy = WRITE | SORTEDSET | FAST; +constexpr uint32_t kZInterStore = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kZInterCard = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kZLexCount = READ | SORTEDSET | FAST; +constexpr uint32_t kZPopMax = WRITE | SORTEDSET | FAST; +constexpr uint32_t kZPopMin = WRITE | SORTEDSET | FAST; +constexpr uint32_t kZRem = WRITE | SORTEDSET | FAST; +constexpr uint32_t kZRange = READ | SORTEDSET | SLOW; +constexpr uint32_t kZRank = READ | SORTEDSET | FAST; +constexpr uint32_t kZRangeByLex = READ | SORTEDSET | SLOW; +constexpr uint32_t kZRangeByScore = READ | SORTEDSET | SLOW; +constexpr uint32_t kZScore = READ | SORTEDSET | FAST; +constexpr uint32_t kZMScore = READ | SORTEDSET | FAST; +constexpr uint32_t kZRemRangeByRank = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kZRemRangeByScore = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kZRemRangeByLex = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kZRevRange = READ | SORTEDSET | SLOW; +constexpr uint32_t kZRevRangeByLex = READ | SORTEDSET | SLOW; +constexpr uint32_t kZRevRangeByScore = READ | SORTEDSET | SLOW; +constexpr uint32_t kZRevRank = READ | SORTEDSET | FAST; +constexpr uint32_t kZScan = READ | SORTEDSET | SLOW; +constexpr uint32_t kZUnion = READ | SORTEDSET | SLOW; +constexpr uint32_t kZUnionStore = WRITE | SORTEDSET | SLOW; +constexpr uint32_t kGeoAdd = WRITE | GEO | SLOW; +constexpr uint32_t kGeoHash = READ | GEO | SLOW; +constexpr uint32_t kGeoPos = READ | GEO | SLOW; +constexpr uint32_t kGeoDist = READ | GEO | SLOW; +} // namespace acl + void ZSetFamily::Register(CommandRegistry* registry) { constexpr uint32_t kStoreMask = CO::WRITE | CO::VARIADIC_KEYS | CO::REVERSE_MAPPING | CO::DENYOOM; *registry - << CI{"ZADD", CO::FAST | CO::WRITE | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(ZAdd) - << CI{"BZPOPMIN", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1} + << CI{"ZADD", CO::FAST | CO::WRITE | CO::DENYOOM, -4, 1, 1, 1, acl::kZAdd}.HFUNC(ZAdd) + << CI{"BZPOPMIN", + CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, + -3, + 1, + -2, + 1, + acl::kBZPopMax} .HFUNC(BZPopMin) - << CI{"BZPOPMAX", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, -3, 1, -2, 1} + << CI{"BZPOPMAX", + CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, + -3, + 1, + -2, + 1, + acl::kBZPopMax} .HFUNC(BZPopMax) - << CI{"ZCARD", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(ZCard) - << CI{"ZCOUNT", CO::FAST | CO::READONLY, 4, 1, 1, 1}.HFUNC(ZCount) - << CI{"ZDIFF", CO::READONLY | CO::VARIADIC_KEYS, -3, 2, 2, 1}.HFUNC(ZDiff) - << CI{"ZINCRBY", CO::FAST | CO::WRITE, 4, 1, 1, 1}.HFUNC(ZIncrBy) - << CI{"ZINTERSTORE", kStoreMask, -4, 3, 3, 1}.HFUNC(ZInterStore) - << CI{"ZINTERCARD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1} + << CI{"ZCARD", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kZCard}.HFUNC(ZCard) + << CI{"ZCOUNT", CO::FAST | CO::READONLY, 4, 1, 1, 1, acl::kZCount}.HFUNC(ZCount) + << CI{"ZDIFF", CO::READONLY | CO::VARIADIC_KEYS, -3, 2, 2, 1, acl::kZDiff}.HFUNC(ZDiff) + << CI{"ZINCRBY", CO::FAST | CO::WRITE, 4, 1, 1, 1, acl::kZIncrBy}.HFUNC(ZIncrBy) + << CI{"ZINTERSTORE", kStoreMask, -4, 3, 3, 1, acl::kZInterStore}.HFUNC(ZInterStore) + << CI{"ZINTERCARD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1, + acl::kZInterCard} .HFUNC(ZInterCard) - << CI{"ZLEXCOUNT", CO::READONLY, 4, 1, 1, 1}.HFUNC(ZLexCount) - << CI{"ZPOPMAX", CO::FAST | CO::WRITE, -2, 1, 1, 1}.HFUNC(ZPopMax) - << CI{"ZPOPMIN", CO::FAST | CO::WRITE, -2, 1, 1, 1}.HFUNC(ZPopMin) - << CI{"ZREM", CO::FAST | CO::WRITE, -3, 1, 1, 1}.HFUNC(ZRem) - << CI{"ZRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRange) - << CI{"ZRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZRank) - << CI{"ZRANGEBYLEX", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRangeByLex) - << CI{"ZRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRangeByScore) - << CI{"ZSCORE", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZScore) - << CI{"ZMSCORE", CO::READONLY | CO::FAST, -3, 1, 1, 1}.HFUNC(ZMScore) - << CI{"ZREMRANGEBYRANK", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByRank) - << CI{"ZREMRANGEBYSCORE", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByScore) - << CI{"ZREMRANGEBYLEX", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByLex) - << CI{"ZREVRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRange) - << CI{"ZREVRANGEBYLEX", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRangeByLex) - << CI{"ZREVRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRangeByScore) - << CI{"ZREVRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZRevRank) - << CI{"ZSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(ZScan) - << CI{"ZUNION", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1}.HFUNC( - ZUnion) - << CI{"ZUNIONSTORE", kStoreMask, -4, 3, 3, 1}.HFUNC(ZUnionStore) + << CI{"ZLEXCOUNT", CO::READONLY, 4, 1, 1, 1, acl::kZLexCount}.HFUNC(ZLexCount) + << CI{"ZPOPMAX", CO::FAST | CO::WRITE, -2, 1, 1, 1, acl::kZPopMax}.HFUNC(ZPopMax) + << CI{"ZPOPMIN", CO::FAST | CO::WRITE, -2, 1, 1, 1, acl::kZPopMin}.HFUNC(ZPopMin) + << CI{"ZREM", CO::FAST | CO::WRITE, -3, 1, 1, 1, acl::kZRem}.HFUNC(ZRem) + << CI{"ZRANGE", CO::READONLY, -4, 1, 1, 1, acl::kZRange}.HFUNC(ZRange) + << CI{"ZRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZRange}.HFUNC(ZRank) + << CI{"ZRANGEBYLEX", CO::READONLY, -4, 1, 1, 1, acl::kZRangeByLex}.HFUNC(ZRangeByLex) + << CI{"ZRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1, acl::kZRangeByScore}.HFUNC(ZRangeByScore) + << CI{"ZSCORE", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZScore}.HFUNC(ZScore) + << CI{"ZMSCORE", CO::READONLY | CO::FAST, -3, 1, 1, 1, acl::kZMScore}.HFUNC(ZMScore) + << CI{"ZREMRANGEBYRANK", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByRank}.HFUNC(ZRemRangeByRank) + << CI{"ZREMRANGEBYSCORE", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByScore}.HFUNC( + ZRemRangeByScore) + << CI{"ZREMRANGEBYLEX", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByLex}.HFUNC(ZRemRangeByLex) + << CI{"ZREVRANGE", CO::READONLY, -4, 1, 1, 1, acl::kZRevRange}.HFUNC(ZRevRange) + << CI{"ZREVRANGEBYLEX", CO::READONLY, -4, 1, 1, 1, acl::kZRevRangeByLex}.HFUNC(ZRevRangeByLex) + << CI{"ZREVRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1, acl::kZRevRangeByScore}.HFUNC( + ZRevRangeByScore) + << CI{"ZREVRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZRevRank}.HFUNC(ZRevRank) + << CI{"ZSCAN", CO::READONLY, -3, 1, 1, 1, acl::kZScan}.HFUNC(ZScan) + << CI{"ZUNION", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1, + acl::kZUnion} + .HFUNC(ZUnion) + << CI{"ZUNIONSTORE", kStoreMask, -4, 3, 3, 1, acl::kZUnionStore}.HFUNC(ZUnionStore) // GEO functions - << CI{"GEOADD", CO::FAST | CO::WRITE | CO::DENYOOM, -5, 1, 1, 1}.HFUNC(GeoAdd) - << CI{"GEOHASH", CO::FAST | CO::READONLY, -2, 1, 1, 1}.HFUNC(GeoHash) - << CI{"GEOPOS", CO::FAST | CO::READONLY, -2, 1, 1, 1}.HFUNC(GeoPos) - << CI{"GEODIST", CO::READONLY, -4, 1, 1, 1}.HFUNC(GeoDist); + << CI{"GEOADD", CO::FAST | CO::WRITE | CO::DENYOOM, -5, 1, 1, 1, acl::kGeoAdd}.HFUNC(GeoAdd) + << CI{"GEOHASH", CO::FAST | CO::READONLY, -2, 1, 1, 1, acl::kGeoHash}.HFUNC(GeoHash) + << CI{"GEOPOS", CO::FAST | CO::READONLY, -2, 1, 1, 1, acl::kGeoPos}.HFUNC(GeoPos) + << CI{"GEODIST", CO::READONLY, -4, 1, 1, 1, acl::kGeoDist}.HFUNC(GeoDist); } } // namespace dfly