chore: add acl categories to all commands (#1711)

1. Add acl categories to each command
2. Extend `facade::CommandId` to include acl category
3. Add dragonfly extensions to acl categories for modules like search, json etc (since modules in redis do not have acl categories by default but in dragonfly these are not implemented as modules)
This commit is contained in:
Kostas Kyrimis 2023-08-20 13:27:14 +03:00 committed by GitHub
parent 98c6aac4e7
commit cfc04cf952
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 709 additions and 355 deletions

View file

@ -7,6 +7,7 @@
#include <string_view> #include <string_view>
#include "facade/facade_types.h" #include "facade/facade_types.h"
#include "server/acl/acl_commands_def.h"
namespace facade { namespace facade {
@ -30,9 +31,10 @@ class CommandId {
* -1 means the last key index is (arg_length - 1), -2 means that the last key * -1 means the last key index is (arg_length - 1), -2 means that the last key
* index is (arg_length - 2). * index is (arg_length - 2).
* @param step - step count for locating repeating keys * @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, 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 { std::string_view name() const {
return name_; return name_;
@ -58,6 +60,10 @@ class CommandId {
return step_key_; return step_key_;
} }
uint32_t acl_categories() const {
return acl_categories_;
}
static uint32_t OptCount(uint32_t mask); static uint32_t OptCount(uint32_t mask);
protected: protected:
@ -68,6 +74,7 @@ class CommandId {
int8_t first_key_; int8_t first_key_;
int8_t last_key_; int8_t last_key_;
int8_t step_key_; int8_t step_key_;
uint32_t acl_categories_;
}; };
} // namespace facade } // namespace facade

View file

@ -140,9 +140,9 @@ RedisReplyBuilder* ConnectionContext::operator->() {
} }
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, 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), : 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) { uint32_t CommandId::OptCount(uint32_t mask) {

View file

@ -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<uint32_t>::max();
inline const absl::flat_hash_map<std::string_view, uint32_t> 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

View file

@ -6,7 +6,7 @@
#include <openssl/sha.h> #include <openssl/sha.h>
namespace dfly { namespace dfly::acl {
namespace { namespace {
std::string StringSHA256(std::string_view password) { std::string StringSHA256(std::string_view password) {
@ -81,4 +81,4 @@ bool User::IsActive() const {
return is_active_; return is_active_;
} }
} // namespace dfly } // namespace dfly::acl

View file

@ -12,8 +12,9 @@
#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_map.h"
#include "absl/hash/hash.h" #include "absl/hash/hash.h"
#include "server/acl/acl_commands_def.h"
namespace dfly { namespace dfly::acl {
class CommandId; class CommandId;
@ -21,87 +22,6 @@ class CommandId;
//#bool CheckIfCommandAllowed(uint64_t command_id, const CommandId& command); //#bool CheckIfCommandAllowed(uint64_t command_id, const CommandId& command);
//#bool CheckIfAclCategoryAllowed(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<uint32_t>::max();
} // namespace AclCat
inline const absl::flat_hash_map<std::string_view, uint32_t> 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 { class User final {
public: public:
struct UpdateRequest { struct UpdateRequest {
@ -153,7 +73,7 @@ class User final {
// when optional is empty, the special `nopass` password is implied // when optional is empty, the special `nopass` password is implied
// password hashed with xx64 // password hashed with xx64
std::optional<std::string> password_hash_; std::optional<std::string> 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 // we have at least 221 commands including a bunch of subcommands
// LARGE_BITFIELD_DATATYPE acl_commands_; // LARGE_BITFIELD_DATATYPE acl_commands_;
@ -162,4 +82,4 @@ class User final {
bool is_active_{false}; bool is_active_{false};
}; };
} // namespace dfly } // namespace dfly::acl

View file

@ -8,7 +8,7 @@
#include "core/fibers.h" #include "core/fibers.h"
namespace dfly { namespace dfly::acl {
void UserRegistry::MaybeAddAndUpdate(std::string_view username, User::UpdateRequest req) { void UserRegistry::MaybeAddAndUpdate(std::string_view username, User::UpdateRequest req) {
std::unique_lock<util::SharedMutex> lock(mu_); std::unique_lock<util::SharedMutex> lock(mu_);
@ -50,4 +50,4 @@ bool UserRegistry::AuthUser(std::string_view username, std::string_view password
return user->second.HasPassword(password); return user->second.HasPassword(password);
} }
} // namespace dfly } // namespace dfly::acl

View file

@ -12,7 +12,7 @@
#include "core/fibers.h" #include "core/fibers.h"
#include "server/acl/user.h" #include "server/acl/user.h"
namespace dfly { namespace dfly::acl {
class UserRegistry { class UserRegistry {
public: public:
@ -54,4 +54,4 @@ class UserRegistry {
mutable util::SharedMutex mu_; mutable util::SharedMutex mu_;
}; };
} // namespace dfly } // namespace dfly::acl

View file

@ -9,11 +9,12 @@
#include "base/gtest.h" #include "base/gtest.h"
#include "base/logging.h" #include "base/logging.h"
#include "server/acl/acl_commands_def.h"
#include "server/acl/user.h" #include "server/acl/user.h"
using namespace testing; using namespace testing;
namespace dfly { namespace dfly::acl {
class UserRegistryTest : public Test {}; class UserRegistryTest : public Test {};
@ -27,19 +28,19 @@ TEST_F(UserRegistryTest, BasicOp) {
CHECK_EQ(registry.AuthUser(username, pass), true); CHECK_EQ(registry.AuthUser(username, pass), true);
CHECK_EQ(registry.IsUserActive(username), false); 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, {}, {}}; req = User::UpdateRequest{{}, set_category, {}, {}};
registry.MaybeAddAndUpdate(username, std::move(req)); registry.MaybeAddAndUpdate(username, std::move(req));
auto acl_categories = registry.GetCredentials(username).acl_categories; auto acl_categories = registry.GetCredentials(username).acl_categories;
CHECK_EQ(acl_categories, set_category); 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)); registry.MaybeAddAndUpdate(username, std::move(req));
acl_categories = registry.GetCredentials(username).acl_categories; 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); CHECK_EQ(acl_categories, expected_res);
} }
} // namespace dfly } // namespace dfly::acl

View file

@ -11,6 +11,7 @@ extern "C" {
} }
#include "base/logging.h" #include "base/logging.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/common.h" #include "server/common.h"
#include "server/conn_context.h" #include "server/conn_context.h"
@ -828,16 +829,27 @@ OpResult<int64_t> FindFirstBitWithValue(const OpArgs& op_args, std::string_view
} // namespace } // 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) { void BitOpsFamily::Register(CommandRegistry* registry) {
using CI = CommandId; using CI = CommandId;
*registry << CI{"BITPOS", CO::CommandOpt::READONLY, -3, 1, 1, 1}.SetHandler(&BitPos) *registry
<< CI{"BITCOUNT", CO::READONLY, -2, 1, 1, 1}.SetHandler(&BitCount) << CI{"BITPOS", CO::CommandOpt::READONLY, -3, 1, 1, 1, acl::kBitPos}.SetHandler(&BitPos)
<< CI{"BITFIELD", CO::WRITE, -3, 1, 1, 1}.SetHandler(&BitField) << CI{"BITCOUNT", CO::READONLY, -2, 1, 1, 1, acl::kBitCount}.SetHandler(&BitCount)
<< CI{"BITFIELD_RO", CO::READONLY, -5, 1, 1, 1}.SetHandler(&BitFieldRo) << CI{"BITFIELD", CO::WRITE, -3, 1, 1, 1, acl::kBitField}.SetHandler(&BitField)
<< CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1}.SetHandler(&BitOp) << CI{"BITFIELD_RO", CO::READONLY, -5, 1, 1, 1, acl::kBitFieldRo}.SetHandler(&BitFieldRo)
<< CI{"GETBIT", CO::READONLY | CO::FAST, 3, 1, 1, 1}.SetHandler(&GetBit) << CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1, acl::kBitOp}.SetHandler(&BitOp)
<< CI{"SETBIT", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1}.SetHandler(&SetBit); << 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 } // namespace dfly

View file

@ -7,6 +7,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "base/logging.h" #include "base/logging.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/engine_shard_set.h" #include "server/engine_shard_set.h"
#include "server/server_state.h" #include "server/server_state.h"
@ -22,7 +23,7 @@ using namespace testing;
class BlockingControllerTest : public Test { class BlockingControllerTest : public Test {
protected: protected:
BlockingControllerTest() : cid_("blpop", 0, -3, 1, -2, 1) { BlockingControllerTest() : cid_("blpop", 0, -3, 1, -2, 1, acl::NONE) {
} }
void SetUp() override; void SetUp() override;
void TearDown() override; void TearDown() override;

View file

@ -14,6 +14,7 @@
#include "core/json_object.h" #include "core/json_object.h"
#include "facade/dragonfly_connection.h" #include "facade/dragonfly_connection.h"
#include "facade/error.h" #include "facade/error.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/dflycmd.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)) #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) { void ClusterFamily::Register(CommandRegistry* registry) {
*registry << CI{"CLUSTER", CO::READONLY, -2, 0, 0, 0}.HFUNC(Cluster) *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}.HFUNC( << CI{"DFLYCLUSTER", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0,
DflyCluster) acl::kDflyCluster}
<< CI{"READONLY", CO::READONLY, 1, 0, 0, 0}.HFUNC(ReadOnly) .HFUNC(DflyCluster)
<< CI{"READWRITE", CO::READONLY, 1, 0, 0, 0}.HFUNC(ReadWrite); << 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 } // namespace dfly

View file

@ -31,8 +31,8 @@ using absl::StrCat;
using absl::StrSplit; using absl::StrSplit;
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, 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)
: facade::CommandId(name, mask, arity, first_key, last_key, step) { : facade::CommandId(name, mask, arity, first_key, last_key, step, acl_categories) {
if (mask & CO::ADMIN) if (mask & CO::ADMIN)
opt_mask_ |= CO::NOSCRIPT; opt_mask_ |= CO::NOSCRIPT;

View file

@ -62,7 +62,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,
int8_t step); int8_t step, uint32_t acl_categories);
CommandId(CommandId&&) = default; CommandId(CommandId&&) = default;

View file

@ -13,6 +13,7 @@ extern "C" {
#include "base/flags.h" #include "base/flags.h"
#include "base/logging.h" #include "base/logging.h"
#include "redis/rdb.h" #include "redis/rdb.h"
#include "server/acl/acl_commands_def.h"
#include "server/blocking_controller.h" #include "server/blocking_controller.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
@ -1445,39 +1446,73 @@ using CI = CommandId;
#define HFUNC(x) SetHandler(&GenericFamily::x) #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) { void GenericFamily::Register(CommandRegistry* registry) {
constexpr auto kSelectOpts = CO::LOADING | CO::FAST | CO::NOSCRIPT; constexpr auto kSelectOpts = CO::LOADING | CO::FAST | CO::NOSCRIPT;
*registry << CI{"DEL", CO::WRITE, -2, 1, -1, 1}.HFUNC(Del) *registry
/* Redis compatibility: << CI{"DEL", CO::WRITE, -2, 1, -1, 1, acl::kDel}.HFUNC(Del)
* We don't allow PING during loading since in Redis PING is used as /* Redis compatibility:
* failure detection, and a loading server is considered to be * We don't allow PING during loading since in Redis PING is used as
* not available. */ * failure detection, and a loading server is considered to be
<< CI{"PING", CO::FAST, -1, 0, 0, 0}.HFUNC(Ping) * not available. */
<< CI{"ECHO", CO::LOADING | CO::FAST, 2, 0, 0, 0}.HFUNC(Echo) << CI{"PING", CO::FAST, -1, 0, 0, 0, acl::kPing}.HFUNC(Ping)
<< CI{"EXISTS", CO::READONLY | CO::FAST, -2, 1, -1, 1}.HFUNC(Exists) << CI{"ECHO", CO::LOADING | CO::FAST, 2, 0, 0, 0, acl::kEcho}.HFUNC(Echo)
<< CI{"TOUCH", CO::READONLY | CO::FAST, -2, 1, -1, 1}.HFUNC(Exists) << CI{"EXISTS", CO::READONLY | CO::FAST, -2, 1, -1, 1, acl::kExists}.HFUNC(Exists)
<< CI{"EXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Expire) << CI{"TOUCH", CO::READONLY | CO::FAST, -2, 1, -1, 1, acl::kTouch}.HFUNC(Exists)
<< CI{"EXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(ExpireAt) << CI{"EXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kExpire}.HFUNC(
<< CI{"PERSIST", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Persist) Expire)
<< CI{"KEYS", CO::READONLY, 2, 0, 0, 0}.HFUNC(Keys) << CI{"EXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kExpireAt}
<< CI{"PEXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC( .HFUNC(ExpireAt)
PexpireAt) << CI{"PERSIST", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kPersist}.HFUNC(Persist)
<< CI{"PEXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Pexpire) << CI{"KEYS", CO::READONLY, 2, 0, 0, 0, acl::kKeys}.HFUNC(Keys)
<< CI{"RENAME", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1}.HFUNC(Rename) << CI{"PEXPIREAT", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kPExpireAt}
<< CI{"RENAMENX", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1}.HFUNC(RenameNx) .HFUNC(PexpireAt)
<< CI{"SELECT", kSelectOpts, 2, 0, 0, 0}.HFUNC(Select) << CI{"PEXPIRE", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 1, 1, acl::kPExpire}.HFUNC(
<< CI{"SCAN", CO::READONLY | CO::FAST | CO::LOADING, -2, 0, 0, 0}.HFUNC(Scan) Pexpire)
<< CI{"TTL", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Ttl) << CI{"RENAME", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRename}.HFUNC(Rename)
<< CI{"PTTL", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Pttl) << CI{"RENAMENX", CO::WRITE | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRenamNX}.HFUNC(RenameNx)
<< CI{"TIME", CO::LOADING | CO::FAST, 1, 0, 0, 0}.HFUNC(Time) << CI{"SELECT", kSelectOpts, 2, 0, 0, 0, acl::kSelect}.HFUNC(Select)
<< CI{"TYPE", CO::READONLY | CO::FAST | CO::LOADING, 2, 1, 1, 1}.HFUNC(Type) << CI{"SCAN", CO::READONLY | CO::FAST | CO::LOADING, -2, 0, 0, 0, acl::kScan}.HFUNC(Scan)
<< CI{"DUMP", CO::READONLY, 2, 1, 1, 1}.HFUNC(Dump) << CI{"TTL", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kTTL}.HFUNC(Ttl)
<< CI{"UNLINK", CO::WRITE, -2, 1, -1, 1}.HFUNC(Del) << CI{"PTTL", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kPTTL}.HFUNC(Pttl)
<< CI{"STICK", CO::WRITE, -2, 1, -1, 1}.HFUNC(Stick) << CI{"TIME", CO::LOADING | CO::FAST, 1, 0, 0, 0, acl::kTime}.HFUNC(Time)
<< CI{"SORT", CO::READONLY, -2, 1, 1, 1}.HFUNC(Sort) << CI{"TYPE", CO::READONLY | CO::FAST | CO::LOADING, 2, 1, 1, 1, acl::kType}.HFUNC(Type)
<< CI{"MOVE", CO::WRITE | CO::GLOBAL_TRANS | CO::NO_AUTOJOURNAL, 3, 1, 1, 1}.HFUNC(Move) << CI{"DUMP", CO::READONLY, 2, 1, 1, 1, acl::kDump}.HFUNC(Dump)
<< CI{"RESTORE", CO::WRITE, -4, 1, 1, 1}.HFUNC(Restore); << 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 } // namespace dfly

View file

@ -4,6 +4,8 @@
#include "server/hll_family.h" #include "server/hll_family.h"
#include "server/acl/acl_commands_def.h"
extern "C" { extern "C" {
#include "redis/hyperloglog.h" #include "redis/hyperloglog.h"
} }
@ -11,6 +13,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "facade/error.h" #include "facade/error.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/container_utils.h" #include "server/container_utils.h"
@ -283,12 +286,18 @@ void PFMerge(CmdArgList args, ConnectionContext* cntx) {
} // namespace } // 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) { void HllFamily::Register(CommandRegistry* registry) {
using CI = CommandId; using CI = CommandId;
*registry << CI{"PFADD", CO::WRITE, -3, 1, 1, 1}.SetHandler(PFAdd) *registry << CI{"PFADD", CO::WRITE, -3, 1, 1, 1, acl::kPFAdd}.SetHandler(PFAdd)
<< CI{"PFCOUNT", CO::WRITE, -2, 1, -1, 1}.SetHandler(PFCount) << CI{"PFCOUNT", CO::WRITE, -2, 1, -1, 1, acl::kPFCount}.SetHandler(PFCount)
<< CI{"PFMERGE", CO::WRITE, -2, 1, -1, 1}.SetHandler(PFMerge); << CI{"PFMERGE", CO::WRITE, -2, 1, -1, 1, acl::kPFMerge}.SetHandler(PFMerge);
} }
const char HllFamily::kInvalidHllErr[] = "Key is not a valid HyperLogLog string value."; const char HllFamily::kInvalidHllErr[] = "Key is not a valid HyperLogLog string value.";

View file

@ -14,6 +14,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "core/string_map.h" #include "core/string_map.h"
#include "facade/error.h" #include "facade/error.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/container_utils.h" #include "server/container_utils.h"
@ -1060,27 +1061,49 @@ using CI = CommandId;
#define HFUNC(x) SetHandler(&HSetFamily::x) #define HFUNC(x) SetHandler(&HSetFamily::x)
void HSetFamily::Register(CommandRegistry* registry) { namespace acl {
*registry << CI{"HDEL", CO::FAST | CO::WRITE, -3, 1, 1, 1}.HFUNC(HDel) constexpr uint32_t kHDel = WRITE | HASH | FAST;
<< CI{"HLEN", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(HLen) constexpr uint32_t kHLen = READ | HASH | FAST;
<< CI{"HEXISTS", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(HExists) constexpr uint32_t kHExists = READ | HASH | FAST;
<< CI{"HGET", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(HGet) constexpr uint32_t kHGet = READ | HASH | FAST;
<< CI{"HGETALL", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(HGetAll) constexpr uint32_t kHGetAll = READ | HASH | SLOW;
<< CI{"HMGET", CO::FAST | CO::READONLY, -3, 1, 1, 1}.HFUNC(HMGet) constexpr uint32_t kHMGet = READ | HASH | FAST;
<< CI{"HMSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(HSet) constexpr uint32_t kHMSet = WRITE | HASH | FAST;
<< CI{"HINCRBY", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC(HIncrBy) constexpr uint32_t kHIncrBy = WRITE | HASH | FAST;
<< CI{"HINCRBYFLOAT", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC( constexpr uint32_t kHIncrByFloat = WRITE | HASH | FAST;
HIncrByFloat) constexpr uint32_t kHKeys = READ | HASH | SLOW;
<< CI{"HKEYS", CO::READONLY, 2, 1, 1, 1}.HFUNC(HKeys) 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 void HSetFamily::Register(CommandRegistry* registry) {
<< CI{"HRANDFIELD", CO::READONLY, 2, 1, 1, 1}.HFUNC(HRandField) *registry
<< CI{"HSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(HScan) << CI{"HDEL", CO::FAST | CO::WRITE, -3, 1, 1, 1, acl::kHDel}.HFUNC(HDel)
<< CI{"HSET", CO::WRITE | CO::FAST | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(HSet) << CI{"HLEN", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kHLen}.HFUNC(HLen)
<< CI{"HSETEX", CO::WRITE | CO::FAST | CO::DENYOOM, -5, 1, 1, 1}.SetHandler(HSetEx) << CI{"HEXISTS", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kHExists}.HFUNC(HExists)
<< CI{"HSETNX", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC(HSetNx) << CI{"HGET", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kHGet}.HFUNC(HGet)
<< CI{"HSTRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(HStrLen) << CI{"HGETALL", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kHGetAll}.HFUNC(HGetAll)
<< CI{"HVALS", CO::READONLY, 2, 1, 1, 1}.HFUNC(HVals); << 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() { uint32_t HSetFamily::MaxListPackLen() {

View file

@ -19,6 +19,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "core/json_object.h" #include "core/json_object.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/error.h" #include "server/error.h"
#include "server/journal/journal.h" #include "server/journal/journal.h"
@ -1788,33 +1789,42 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
#define HFUNC(x) SetHandler(&JsonFamily::x) #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) { void JsonFamily::Register(CommandRegistry* registry) {
*registry << CI{"JSON.GET", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Get); *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}.HFUNC( *registry << CI{"JSON.MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -3, 1, -2, 1,
MGet); acl::JSON}
*registry << CI{"JSON.TYPE", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(Type); .HFUNC(MGet);
*registry << CI{"JSON.STRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(StrLen); *registry << CI{"JSON.TYPE", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Type);
*registry << CI{"JSON.OBJLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ObjLen); *registry << CI{"JSON.STRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(StrLen);
*registry << CI{"JSON.ARRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ArrLen); *registry << CI{"JSON.OBJLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ObjLen);
*registry << CI{"JSON.TOGGLE", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(Toggle); *registry << CI{"JSON.ARRLEN", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ArrLen);
*registry << CI{"JSON.NUMINCRBY", CO::WRITE | CO::FAST, 4, 1, 1, 1}.HFUNC(NumIncrBy); *registry << CI{"JSON.TOGGLE", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Toggle);
*registry << CI{"JSON.NUMMULTBY", CO::WRITE | CO::FAST, 4, 1, 1, 1}.HFUNC(NumMultBy); *registry << CI{"JSON.NUMINCRBY", CO::WRITE | CO::FAST, 4, 1, 1, 1, acl::JSON}.HFUNC(NumIncrBy);
*registry << CI{"JSON.DEL", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del); *registry << CI{"JSON.NUMMULTBY", CO::WRITE | CO::FAST, 4, 1, 1, 1, acl::JSON}.HFUNC(NumMultBy);
*registry << CI{"JSON.FORGET", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del); // An alias of JSON.DEL. *registry << CI{"JSON.DEL", CO::WRITE, -2, 1, 1, 1, acl::JSON}.HFUNC(Del);
*registry << CI{"JSON.OBJKEYS", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ObjKeys); *registry << CI{"JSON.FORGET", CO::WRITE, -2, 1, 1, 1, acl::JSON}.HFUNC(
*registry << CI{"JSON.STRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( Del); // An alias of JSON.DEL.
StrAppend); *registry << CI{"JSON.OBJKEYS", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(ObjKeys);
*registry << CI{"JSON.CLEAR", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(Clear); *registry << CI{"JSON.STRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON}
*registry << CI{"JSON.ARRPOP", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(ArrPop); .HFUNC(StrAppend);
*registry << CI{"JSON.ARRTRIM", CO::WRITE | CO::FAST, 5, 1, 1, 1}.HFUNC(ArrTrim); *registry << CI{"JSON.CLEAR", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::JSON}.HFUNC(Clear);
*registry << CI{"JSON.ARRINSERT", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( *registry << CI{"JSON.ARRPOP", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::JSON}.HFUNC(ArrPop);
ArrInsert); *registry << CI{"JSON.ARRTRIM", CO::WRITE | CO::FAST, 5, 1, 1, 1, acl::JSON}.HFUNC(ArrTrim);
*registry << CI{"JSON.ARRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC( *registry << CI{"JSON.ARRINSERT", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON}
ArrAppend); .HFUNC(ArrInsert);
*registry << CI{"JSON.ARRINDEX", CO::READONLY | CO::FAST, -4, 1, 1, 1}.HFUNC(ArrIndex); *registry << CI{"JSON.ARRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1, acl::JSON}
*registry << CI{"JSON.DEBUG", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Debug); .HFUNC(ArrAppend);
*registry << CI{"JSON.RESP", CO::READONLY | CO::FAST, -2, 1, 1, 1}.HFUNC(Resp); *registry << CI{"JSON.ARRINDEX", CO::READONLY | CO::FAST, -4, 1, 1, 1, acl::JSON}.HFUNC(ArrIndex);
*registry << CI{"JSON.SET", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC(Set); *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 } // namespace dfly

View file

@ -3,6 +3,8 @@
// //
#include "server/list_family.h" #include "server/list_family.h"
#include "server/acl/acl_commands_def.h"
extern "C" { extern "C" {
#include "redis/object.h" #include "redis/object.h"
#include "redis/sds.h" #include "redis/sds.h"
@ -1296,32 +1298,64 @@ using CI = CommandId;
#define HFUNC(x) SetHandler(&ListFamily::x) #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) { void ListFamily::Register(CommandRegistry* registry) {
*registry *registry
<< CI{"LPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(LPush) << 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}.HFUNC(LPushX) << 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}.HFUNC(LPop) << 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}.HFUNC(RPush) << 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}.HFUNC(RPushX) << 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}.HFUNC(RPop) << 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}.SetHandler( << CI{"RPOPLPUSH", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, 3, 1, 2, 1, acl::kRPopLPush}
RPopLPush) .SetHandler(RPopLPush)
<< CI{"BRPOPLPUSH", CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL, 4, 1, 2, 1} << CI{"BRPOPLPUSH",
CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL,
4,
1,
2,
1,
acl::kBRPopLPush}
.SetHandler(BRPopLPush) .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) .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) .HFUNC(BRPop)
<< CI{"LLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(LLen) << CI{"LLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kLLen}.HFUNC(LLen)
<< CI{"LPOS", CO::READONLY | CO::FAST, -3, 1, 1, 1}.HFUNC(LPos) << CI{"LPOS", CO::READONLY | CO::FAST, -3, 1, 1, 1, acl::kLPos}.HFUNC(LPos)
<< CI{"LINDEX", CO::READONLY, 3, 1, 1, 1}.HFUNC(LIndex) << CI{"LINDEX", CO::READONLY, 3, 1, 1, 1, acl::kLIndex}.HFUNC(LIndex)
<< CI{"LINSERT", CO::WRITE | CO::DENYOOM, 5, 1, 1, 1}.HFUNC(LInsert) << CI{"LINSERT", CO::WRITE | CO::DENYOOM, 5, 1, 1, 1, acl::kLInsert}.HFUNC(LInsert)
<< CI{"LRANGE", CO::READONLY, 4, 1, 1, 1}.HFUNC(LRange) << CI{"LRANGE", CO::READONLY, 4, 1, 1, 1, acl::kLRange}.HFUNC(LRange)
<< CI{"LSET", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1}.HFUNC(LSet) << CI{"LSET", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1, acl::kLSet}.HFUNC(LSet)
<< CI{"LTRIM", CO::WRITE, 4, 1, 1, 1}.HFUNC(LTrim) << CI{"LTRIM", CO::WRITE, 4, 1, 1, 1, acl::kLTrim}.HFUNC(LTrim)
<< CI{"LREM", CO::WRITE, 4, 1, 1, 1}.HFUNC(LRem) << CI{"LREM", CO::WRITE, 4, 1, 1, 1, acl::kLRem}.HFUNC(LRem)
<< CI{"LMOVE", CO::WRITE | CO::NO_AUTOJOURNAL, 5, 1, 2, 1}.HFUNC(LMove) << 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}.SetHandler(BLMove); << CI{"BLMOVE", CO::WRITE | CO::NO_AUTOJOURNAL | CO::BLOCKING, 6, 1, 2, 1, acl::kBLMove}
.SetHandler(BLMove);
} }
} // namespace dfly } // namespace dfly

View file

@ -22,6 +22,7 @@ extern "C" {
#include "facade/dragonfly_connection.h" #include "facade/dragonfly_connection.h"
#include "facade/error.h" #include "facade/error.h"
#include "facade/reply_capture.h" #include "facade/reply_capture.h"
#include "server/acl/acl_commands_def.h"
#include "server/bitops_family.h" #include "server/bitops_family.h"
#include "server/cluster/cluster_family.h" #include "server/cluster/cluster_family.h"
#include "server/conn_context.h" #include "server/conn_context.h"
@ -2019,29 +2020,55 @@ using ServiceFunc = void (Service::*)(CmdArgList, ConnectionContext* cntx);
#define MFUNC(x) \ #define MFUNC(x) \
SetHandler([this](CmdArgList sp, ConnectionContext* cntx) { this->x(std::move(sp), cntx); }) 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() { void Service::RegisterCommands() {
using CI = CommandId; using CI = CommandId;
registry_ registry_
<< CI{"QUIT", CO::READONLY | CO::FAST, 1, 0, 0, 0}.HFUNC(Quit) << 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}.HFUNC(Multi) << CI{"MULTI", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kMulti}.HFUNC(Multi)
<< CI{"WATCH", CO::LOADING, -2, 1, -1, 1}.HFUNC(Watch) << CI{"WATCH", CO::LOADING, -2, 1, -1, 1, acl::kWatch}.HFUNC(Watch)
<< CI{"UNWATCH", CO::LOADING, 1, 0, 0, 0}.HFUNC(Unwatch) << CI{"UNWATCH", CO::LOADING, 1, 0, 0, 0, acl::kUnwatch}.HFUNC(Unwatch)
<< CI{"DISCARD", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0}.MFUNC(Discard) << CI{"DISCARD", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kDiscard}.MFUNC(
<< CI{"EVAL", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1}.MFUNC(Eval).SetValidator( Discard)
&EvalValidator) << CI{"EVAL", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1, acl::kEval}
<< CI{"EVALSHA", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1}.MFUNC(EvalSha).SetValidator( .MFUNC(Eval)
&EvalValidator) .SetValidator(&EvalValidator)
<< CI{"EXEC", CO::LOADING | CO::NOSCRIPT, 1, 0, 0, 1}.MFUNC(Exec) << CI{"EVALSHA", CO::NOSCRIPT | CO::VARIADIC_KEYS, -3, 3, 3, 1, acl::kEvalSha}
<< CI{"PUBLISH", CO::LOADING | CO::FAST, 3, 0, 0, 0}.MFUNC(Publish) .MFUNC(EvalSha)
<< CI{"SUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(Subscribe) .SetValidator(&EvalValidator)
<< CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.MFUNC(Unsubscribe) << CI{"EXEC", CO::LOADING | CO::NOSCRIPT, 1, 0, 0, 1, acl::kExec}.MFUNC(Exec)
<< CI{"PSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(PSubscribe) << CI{"PUBLISH", CO::LOADING | CO::FAST, 3, 0, 0, 0, acl::kPublish}.MFUNC(Publish)
<< CI{"PUNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.MFUNC(PUnsubscribe) << CI{"SUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kSubscribe}.MFUNC(Subscribe)
<< CI{"FUNCTION", CO::NOSCRIPT, 2, 0, 0, 0}.MFUNC(Function) << CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0, acl::kUnsubscribe}.MFUNC(
<< CI{"MONITOR", CO::ADMIN, 1, 0, 0, 0}.MFUNC(Monitor) Unsubscribe)
<< CI{"PUBSUB", CO::LOADING | CO::FAST, -1, 0, 0, 0}.MFUNC(Pubsub) << CI{"PSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kPSubscribe}.MFUNC(
<< CI{"COMMAND", CO::LOADING | CO::NOSCRIPT, -1, 0, 0, 0}.MFUNC(Command); 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(&registry_); StreamFamily::Register(&registry_);
StringFamily::Register(&registry_); StringFamily::Register(&registry_);

View file

@ -18,6 +18,7 @@
#include "core/search/search.h" #include "core/search/search.h"
#include "facade/error.h" #include "facade/error.h"
#include "facade/reply_builder.h" #include "facade/reply_builder.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/container_utils.h" #include "server/container_utils.h"
@ -373,15 +374,19 @@ void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) {
#define HFUNC(x) SetHandler(&SearchFamily::x) #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) { void SearchFamily::Register(CommandRegistry* registry) {
using CI = CommandId; using CI = CommandId;
*registry << CI{"FT.CREATE", CO::GLOBAL_TRANS, -2, 0, 0, 0}.HFUNC(FtCreate) *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}.HFUNC(FtDropIndex) << 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}.HFUNC(FtInfo) << 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) // 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._LIST", CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtList)
<< CI{"FT.SEARCH", CO::GLOBAL_TRANS, -3, 0, 0, 0}.HFUNC(FtSearch); << CI{"FT.SEARCH", CO::GLOBAL_TRANS, -3, 0, 0, 0, acl::FT_SEARCH}.HFUNC(FtSearch);
} }
} // namespace dfly } // namespace dfly

View file

@ -28,6 +28,7 @@ extern "C" {
#include "facade/reply_builder.h" #include "facade/reply_builder.h"
#include "io/file_util.h" #include "io/file_util.h"
#include "io/proc_reader.h" #include "io/proc_reader.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/debugcmd.h" #include "server/debugcmd.h"
@ -2414,34 +2415,63 @@ void ServerFamily::Dfly(CmdArgList args, ConnectionContext* cntx) {
#define HFUNC(x) SetHandler(HandlerFunc(this, &ServerFamily::x)) #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) { void ServerFamily::Register(CommandRegistry* registry) {
constexpr auto kReplicaOpts = CO::LOADING | CO::ADMIN | CO::GLOBAL_TRANS; constexpr auto kReplicaOpts = CO::LOADING | CO::ADMIN | CO::GLOBAL_TRANS;
constexpr auto kMemOpts = CO::LOADING | CO::READONLY | CO::FAST | CO::NOSCRIPT; 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) *registry
<< CI{"BGSAVE", CO::ADMIN | CO::GLOBAL_TRANS, 1, 0, 0, 0}.HFUNC(Save) << CI{"AUTH", CO::NOSCRIPT | CO::FAST | CO::LOADING, -2, 0, 0, 0, acl::kAuth}.HFUNC(Auth)
<< CI{"CLIENT", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.HFUNC(Client) << CI{"BGSAVE", CO::ADMIN | CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::kBGSave}.HFUNC(Save)
<< CI{"CONFIG", CO::ADMIN, -2, 0, 0, 0}.HFUNC(Config) << CI{"CLIENT", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0, acl::kClient}.HFUNC(Client)
<< CI{"DBSIZE", CO::READONLY | CO::FAST | CO::LOADING, 1, 0, 0, 0}.HFUNC(DbSize) << CI{"CONFIG", CO::ADMIN, -2, 0, 0, 0, acl::kConfig}.HFUNC(Config)
<< CI{"DEBUG", CO::ADMIN | CO::LOADING, -2, 0, 0, 0}.HFUNC(Debug) << CI{"DBSIZE", CO::READONLY | CO::FAST | CO::LOADING, 1, 0, 0, 0, acl::kDbSize}.HFUNC(DbSize)
<< CI{"FLUSHDB", CO::WRITE | CO::GLOBAL_TRANS, 1, 0, 0, 0}.HFUNC(FlushDb) << CI{"DEBUG", CO::ADMIN | CO::LOADING, -2, 0, 0, 0, acl::kDebug}.HFUNC(Debug)
<< CI{"FLUSHALL", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0}.HFUNC(FlushAll) << CI{"FLUSHDB", CO::WRITE | CO::GLOBAL_TRANS, 1, 0, 0, 0, acl::kFlushDB}.HFUNC(FlushDb)
<< CI{"INFO", CO::LOADING, -1, 0, 0, 0}.HFUNC(Info) << CI{"FLUSHALL", CO::WRITE | CO::GLOBAL_TRANS, -1, 0, 0, 0, acl::kFlushAll}.HFUNC(FlushAll)
<< CI{"HELLO", CO::LOADING, -1, 0, 0, 0}.HFUNC(Hello) << CI{"INFO", CO::LOADING, -1, 0, 0, 0, acl::kInfo}.HFUNC(Info)
<< CI{"LASTSAVE", CO::LOADING | CO::FAST, 1, 0, 0, 0}.HFUNC(LastSave) << CI{"HELLO", CO::LOADING, -1, 0, 0, 0, acl::kHello}.HFUNC(Hello)
<< CI{"LATENCY", CO::NOSCRIPT | CO::LOADING | CO::FAST, -2, 0, 0, 0}.HFUNC(Latency) << CI{"LASTSAVE", CO::LOADING | CO::FAST, 1, 0, 0, 0, acl::kLastSave}.HFUNC(LastSave)
<< CI{"MEMORY", kMemOpts, -2, 0, 0, 0}.HFUNC(Memory) << CI{"LATENCY", CO::NOSCRIPT | CO::LOADING | CO::FAST, -2, 0, 0, 0, acl::kLatency}.HFUNC(
<< CI{"SAVE", CO::ADMIN | CO::GLOBAL_TRANS, -1, 0, 0, 0}.HFUNC(Save) Latency)
<< CI{"SHUTDOWN", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0}.HFUNC( << CI{"MEMORY", kMemOpts, -2, 0, 0, 0, acl::kMemory}.HFUNC(Memory)
ShutdownCmd) << CI{"SAVE", CO::ADMIN | CO::GLOBAL_TRANS, -1, 0, 0, 0, acl::kSave}.HFUNC(Save)
<< CI{"SLAVEOF", kReplicaOpts, 3, 0, 0, 0}.HFUNC(ReplicaOf) << CI{"SHUTDOWN", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, -1, 0, 0, 0, acl::kShutDown}.HFUNC(
<< CI{"REPLICAOF", kReplicaOpts, 3, 0, 0, 0}.HFUNC(ReplicaOf) ShutdownCmd)
<< CI{"REPLTAKEOVER", CO::ADMIN | CO::GLOBAL_TRANS, 2, 0, 0, 0}.HFUNC(ReplTakeOver) << CI{"SLAVEOF", kReplicaOpts, 3, 0, 0, 0, acl::kSlaveOf}.HFUNC(ReplicaOf)
<< CI{"REPLCONF", CO::ADMIN | CO::LOADING, -1, 0, 0, 0}.HFUNC(ReplConf) << CI{"REPLICAOF", kReplicaOpts, 3, 0, 0, 0, acl::kReplicaOf}.HFUNC(ReplicaOf)
<< CI{"ROLE", CO::LOADING | CO::FAST | CO::NOSCRIPT, 1, 0, 0, 0}.HFUNC(Role) << CI{"REPLTAKEOVER", CO::ADMIN | CO::GLOBAL_TRANS, 2, 0, 0, 0, acl::kReplTakeOver}.HFUNC(
<< CI{"SLOWLOG", CO::ADMIN | CO::FAST, -2, 0, 0, 0}.SetHandler(SlowLog) ReplTakeOver)
<< CI{"SCRIPT", CO::NOSCRIPT | CO::NO_KEY_JOURNAL, -2, 0, 0, 0}.HFUNC(Script) << CI{"REPLCONF", CO::ADMIN | CO::LOADING, -1, 0, 0, 0, acl::kReplConf}.HFUNC(ReplConf)
<< CI{"DFLY", CO::ADMIN | CO::GLOBAL_TRANS | CO::HIDDEN, -2, 0, 0, 0}.HFUNC(Dfly); << 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 } // namespace dfly

View file

@ -15,6 +15,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "core/string_set.h" #include "core/string_set.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/container_utils.h" #include "server/container_utils.h"
@ -1560,28 +1561,52 @@ using CI = CommandId;
#define HFUNC(x) SetHandler(&x) #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) { void SetFamily::Register(CommandRegistry* registry) {
*registry << CI{"SADD", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(SAdd) *registry
<< CI{"SDIFF", CO::READONLY, -2, 1, -1, 1}.HFUNC(SDiff) << CI{"SADD", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1, acl::kSAdd}.HFUNC(SAdd)
<< CI{"SDIFFSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( << CI{"SDIFF", CO::READONLY, -2, 1, -1, 1, acl::kSDiff}.HFUNC(SDiff)
SDiffStore) << CI{"SDIFFSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1,
<< CI{"SINTER", CO::READONLY, -2, 1, -1, 1}.HFUNC(SInter) acl::kSDiffStore}
<< CI{"SINTERSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( .HFUNC(SDiffStore)
SInterStore) << CI{"SINTER", CO::READONLY, -2, 1, -1, 1, acl::kSInter}.HFUNC(SInter)
<< CI{"SMEMBERS", CO::READONLY, 2, 1, 1, 1}.HFUNC(SMembers) << CI{"SINTERSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1,
<< CI{"SISMEMBER", CO::FAST | CO::READONLY, 3, 1, 1, 1}.HFUNC(SIsMember) acl::kSInterStore}
<< CI{"SMISMEMBER", CO::READONLY, -3, 1, 1, 1}.HFUNC(SMIsMember) .HFUNC(SInterStore)
<< CI{"SMOVE", CO::FAST | CO::WRITE | CO::NO_AUTOJOURNAL, 4, 1, 2, 1}.HFUNC(SMove) << CI{"SMEMBERS", CO::READONLY, 2, 1, 1, 1, acl::kSMembers}.HFUNC(SMembers)
<< CI{"SREM", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(SRem) << CI{"SISMEMBER", CO::FAST | CO::READONLY, 3, 1, 1, 1, acl::kSIsMember}.HFUNC(SIsMember)
<< CI{"SCARD", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(SCard) << CI{"SMISMEMBER", CO::READONLY, -3, 1, 1, 1, acl::kSMIsMember}.HFUNC(SMIsMember)
<< CI{"SPOP", CO::WRITE | CO::FAST | CO::NO_AUTOJOURNAL, -2, 1, 1, 1}.HFUNC(SPop) << CI{"SMOVE", CO::FAST | CO::WRITE | CO::NO_AUTOJOURNAL, 4, 1, 2, 1, acl::kSMove}.HFUNC(
<< CI{"SUNION", CO::READONLY, -2, 1, -1, 1}.HFUNC(SUnion) SMove)
<< CI{"SUNIONSTORE", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, -1, 1}.HFUNC( << CI{"SREM", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::kSRem}.HFUNC(SRem)
SUnionStore) << CI{"SCARD", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kSCard}.HFUNC(SCard)
<< CI{"SSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(SScan); << 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)) { 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);
} }
} }

View file

@ -13,6 +13,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "facade/error.h" #include "facade/error.h"
#include "server/acl/acl_commands_def.h"
#include "server/blocking_controller.h" #include "server/blocking_controller.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.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) #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) { void StreamFamily::Register(CommandRegistry* registry) {
using CI = CommandId; using CI = CommandId;
*registry << CI{"XADD", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1}.HFUNC(XAdd) *registry
<< CI{"XDEL", CO::WRITE | CO::FAST, -3, 1, 1, 1}.HFUNC(XDel) << CI{"XADD", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1, acl::kXAdd}.HFUNC(XAdd)
<< CI{"XGROUP", CO::WRITE | CO::DENYOOM, -3, 2, 2, 1}.HFUNC(XGroup) << CI{"XDEL", CO::WRITE | CO::FAST, -3, 1, 1, 1, acl::kXDel}.HFUNC(XDel)
<< CI{"XINFO", CO::READONLY | CO::NOSCRIPT, -2, 0, 0, 0}.HFUNC(XInfo) << CI{"XGROUP", CO::WRITE | CO::DENYOOM, -3, 2, 2, 1, acl::kXGroup}.HFUNC(XGroup)
<< CI{"XLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(XLen) << CI{"XINFO", CO::READONLY | CO::NOSCRIPT, -2, 0, 0, 0, acl::kXInfo}.HFUNC(XInfo)
<< CI{"XRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(XRange) << CI{"XLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kXLen}.HFUNC(XLen)
<< CI{"XREVRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(XRevRange) << CI{"XRANGE", CO::READONLY, -4, 1, 1, 1, acl::kXRange}.HFUNC(XRange)
<< CI{"XREAD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 3, 3, 1} << CI{"XREVRANGE", CO::READONLY, -4, 1, 1, 1, acl::kXRevRange}.HFUNC(XRevRange)
.HFUNC(XRead) << CI{"XREAD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 3, 3, 1,
<< CI{"XREADGROUP", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -6, 6, 6, 1} acl::kXRead}
.HFUNC(XReadGroup) .HFUNC(XRead)
<< CI{"XSETID", CO::WRITE, 3, 1, 1, 1}.HFUNC(XSetId) << CI{"XREADGROUP", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -6, 6, 6, 1,
<< CI{"XTRIM", CO::WRITE | CO::FAST, -4, 1, 1, 1}.HFUNC(XTrim) acl::kXReadGroup}
<< CI{"_XGROUP_HELP", CO::NOSCRIPT | CO::HIDDEN, 2, 0, 0, 0}.SetHandler(XGroupHelp); .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 } // namespace dfly

View file

@ -19,6 +19,7 @@ extern "C" {
#include "base/logging.h" #include "base/logging.h"
#include "redis/util.h" #include "redis/util.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h" #include "server/command_registry.h"
#include "server/conn_context.h" #include "server/conn_context.h"
#include "server/engine_shard_set.h" #include "server/engine_shard_set.h"
@ -1467,32 +1468,67 @@ void StringFamily::Shutdown() {
#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) {
*registry *registry
<< CI{"SET", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, -3, 1, 1, 1}.HFUNC(Set) << 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}.HFUNC(SetEx) << CI{"SETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1, acl::kSetEx}.HFUNC(
<< CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1}.HFUNC(PSetEx) SetEx)
<< CI{"SETNX", CO::WRITE | CO::DENYOOM, 3, 1, 1, 1}.HFUNC(SetNx) << CI{"PSETEX", CO::WRITE | CO::DENYOOM | CO::NO_AUTOJOURNAL, 4, 1, 1, 1, acl::kPSetEx}.HFUNC(
<< CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(Append) PSetEx)
<< CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(Prepend) << CI{"SETNX", CO::WRITE | CO::DENYOOM, 3, 1, 1, 1, acl::kSetNx}.HFUNC(SetNx)
<< CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Incr) << CI{"APPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kAppend}.HFUNC(Append)
<< CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(Decr) << CI{"PREPEND", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kPrepend}.HFUNC(Prepend)
<< CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(IncrBy) << CI{"INCR", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kIncr}.HFUNC(Incr)
<< CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(IncrByFloat) << CI{"DECR", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kDecr}.HFUNC(Decr)
<< CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1}.HFUNC(DecrBy) << CI{"INCRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kIncrBy}.HFUNC(IncrBy)
<< CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(Get) << CI{"INCRBYFLOAT", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kIncrByFloat}.HFUNC(IncrByFloat)
<< CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1, 1}.HFUNC(GetDel) << CI{"DECRBY", CO::WRITE | CO::FAST, 3, 1, 1, 1, acl::kDecrBy}.HFUNC(DecrBy)
<< CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -1, 1, 1, 1}.HFUNC( << CI{"GET", CO::READONLY | CO::FAST, 2, 1, 1, 1, acl::kGet}.HFUNC(Get)
GetEx) << CI{"GETDEL", CO::WRITE | CO::FAST, 2, 1, 1, 1, acl::kGetDel}.HFUNC(GetDel)
<< CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(GetSet) << CI{"GETEX", CO::WRITE | CO::DENYOOM | CO::FAST | CO::NO_AUTOJOURNAL, -1, 1, 1, 1,
<< CI{"MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -2, 1, -1, 1}.HFUNC(MGet) acl::kGetEx}
<< CI{"MSET", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2}.HFUNC(MSet) .HFUNC(GetEx)
<< CI{"MSETNX", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2}.HFUNC(MSetNx) << CI{"GETSET", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1, acl::kGetSet}.HFUNC(GetSet)
<< CI{"STRLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(StrLen) << CI{"MGET", CO::READONLY | CO::FAST | CO::REVERSE_MAPPING, -2, 1, -1, 1, acl::kMGet}.HFUNC(
<< CI{"GETRANGE", CO::READONLY | CO::FAST, 4, 1, 1, 1}.HFUNC(GetRange) MGet)
<< CI{"SUBSTR", CO::READONLY | CO::FAST, 4, 1, 1, 1}.HFUNC(GetRange) // Alias for GetRange << CI{"MSET", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2, acl::kMSet}.HFUNC(MSet)
<< CI{"SETRANGE", CO::WRITE | CO::FAST | CO::DENYOOM, 4, 1, 1, 1}.HFUNC(SetRange) << CI{"MSETNX", CO::WRITE | CO::DENYOOM, -3, 1, -1, 2, acl::kMSetNx}.HFUNC(MSetNx)
<< CI{"CL.THROTTLE", CO::WRITE | CO::DENYOOM | CO::FAST, -5, 1, 1, 1}.HFUNC(ClThrottle); << 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 } // namespace dfly

View file

@ -4,6 +4,8 @@
#include "server/test_utils.h" #include "server/test_utils.h"
#include "server/acl/acl_commands_def.h"
extern "C" { extern "C" {
#include "redis/zmalloc.h" #include "redis/zmalloc.h"
} }
@ -67,7 +69,7 @@ void TestConnection::SendPubMessageAsync(PubMessage pmsg) {
} }
void TransactionSuspension::Start() { 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}; transaction_ = new dfly::Transaction{&cid};

View file

@ -4,6 +4,8 @@
#include "server/zset_family.h" #include "server/zset_family.h"
#include "server/acl/acl_commands_def.h"
extern "C" { extern "C" {
#include "redis/geohash.h" #include "redis/geohash.h"
#include "redis/geohash_helper.h" #include "redis/geohash_helper.h"
@ -2534,49 +2536,101 @@ void ZSetFamily::GeoDist(CmdArgList args, ConnectionContext* cntx) {
#define HFUNC(x) SetHandler(&ZSetFamily::x) #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) { void ZSetFamily::Register(CommandRegistry* registry) {
constexpr uint32_t kStoreMask = CO::WRITE | CO::VARIADIC_KEYS | CO::REVERSE_MAPPING | CO::DENYOOM; constexpr uint32_t kStoreMask = CO::WRITE | CO::VARIADIC_KEYS | CO::REVERSE_MAPPING | CO::DENYOOM;
*registry *registry
<< CI{"ZADD", CO::FAST | CO::WRITE | CO::DENYOOM, -4, 1, 1, 1}.HFUNC(ZAdd) << 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} << CI{"BZPOPMIN",
CO::WRITE | CO::NOSCRIPT | CO::BLOCKING | CO::NO_AUTOJOURNAL,
-3,
1,
-2,
1,
acl::kBZPopMax}
.HFUNC(BZPopMin) .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) .HFUNC(BZPopMax)
<< CI{"ZCARD", CO::FAST | CO::READONLY, 2, 1, 1, 1}.HFUNC(ZCard) << CI{"ZCARD", CO::FAST | CO::READONLY, 2, 1, 1, 1, acl::kZCard}.HFUNC(ZCard)
<< CI{"ZCOUNT", CO::FAST | CO::READONLY, 4, 1, 1, 1}.HFUNC(ZCount) << 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}.HFUNC(ZDiff) << 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}.HFUNC(ZIncrBy) << CI{"ZINCRBY", CO::FAST | CO::WRITE, 4, 1, 1, 1, acl::kZIncrBy}.HFUNC(ZIncrBy)
<< CI{"ZINTERSTORE", kStoreMask, -4, 3, 3, 1}.HFUNC(ZInterStore) << 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} << CI{"ZINTERCARD", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1,
acl::kZInterCard}
.HFUNC(ZInterCard) .HFUNC(ZInterCard)
<< CI{"ZLEXCOUNT", CO::READONLY, 4, 1, 1, 1}.HFUNC(ZLexCount) << CI{"ZLEXCOUNT", CO::READONLY, 4, 1, 1, 1, acl::kZLexCount}.HFUNC(ZLexCount)
<< CI{"ZPOPMAX", CO::FAST | CO::WRITE, -2, 1, 1, 1}.HFUNC(ZPopMax) << CI{"ZPOPMAX", CO::FAST | CO::WRITE, -2, 1, 1, 1, acl::kZPopMax}.HFUNC(ZPopMax)
<< CI{"ZPOPMIN", CO::FAST | CO::WRITE, -2, 1, 1, 1}.HFUNC(ZPopMin) << CI{"ZPOPMIN", CO::FAST | CO::WRITE, -2, 1, 1, 1, acl::kZPopMin}.HFUNC(ZPopMin)
<< CI{"ZREM", CO::FAST | CO::WRITE, -3, 1, 1, 1}.HFUNC(ZRem) << CI{"ZREM", CO::FAST | CO::WRITE, -3, 1, 1, 1, acl::kZRem}.HFUNC(ZRem)
<< CI{"ZRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRange) << CI{"ZRANGE", CO::READONLY, -4, 1, 1, 1, acl::kZRange}.HFUNC(ZRange)
<< CI{"ZRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZRank) << CI{"ZRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZRange}.HFUNC(ZRank)
<< CI{"ZRANGEBYLEX", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRangeByLex) << CI{"ZRANGEBYLEX", CO::READONLY, -4, 1, 1, 1, acl::kZRangeByLex}.HFUNC(ZRangeByLex)
<< CI{"ZRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRangeByScore) << CI{"ZRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1, acl::kZRangeByScore}.HFUNC(ZRangeByScore)
<< CI{"ZSCORE", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZScore) << CI{"ZSCORE", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZScore}.HFUNC(ZScore)
<< CI{"ZMSCORE", CO::READONLY | CO::FAST, -3, 1, 1, 1}.HFUNC(ZMScore) << CI{"ZMSCORE", CO::READONLY | CO::FAST, -3, 1, 1, 1, acl::kZMScore}.HFUNC(ZMScore)
<< CI{"ZREMRANGEBYRANK", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByRank) << CI{"ZREMRANGEBYRANK", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByRank}.HFUNC(ZRemRangeByRank)
<< CI{"ZREMRANGEBYSCORE", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByScore) << CI{"ZREMRANGEBYSCORE", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByScore}.HFUNC(
<< CI{"ZREMRANGEBYLEX", CO::WRITE, 4, 1, 1, 1}.HFUNC(ZRemRangeByLex) ZRemRangeByScore)
<< CI{"ZREVRANGE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRange) << CI{"ZREMRANGEBYLEX", CO::WRITE, 4, 1, 1, 1, acl::kZRemRangeByLex}.HFUNC(ZRemRangeByLex)
<< CI{"ZREVRANGEBYLEX", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRangeByLex) << CI{"ZREVRANGE", CO::READONLY, -4, 1, 1, 1, acl::kZRevRange}.HFUNC(ZRevRange)
<< CI{"ZREVRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1}.HFUNC(ZRevRangeByScore) << CI{"ZREVRANGEBYLEX", CO::READONLY, -4, 1, 1, 1, acl::kZRevRangeByLex}.HFUNC(ZRevRangeByLex)
<< CI{"ZREVRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ZRevRank) << CI{"ZREVRANGEBYSCORE", CO::READONLY, -4, 1, 1, 1, acl::kZRevRangeByScore}.HFUNC(
<< CI{"ZSCAN", CO::READONLY, -3, 1, 1, 1}.HFUNC(ZScan) ZRevRangeByScore)
<< CI{"ZUNION", CO::READONLY | CO::REVERSE_MAPPING | CO::VARIADIC_KEYS, -3, 2, 2, 1}.HFUNC( << CI{"ZREVRANK", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kZRevRank}.HFUNC(ZRevRank)
ZUnion) << CI{"ZSCAN", CO::READONLY, -3, 1, 1, 1, acl::kZScan}.HFUNC(ZScan)
<< CI{"ZUNIONSTORE", kStoreMask, -4, 3, 3, 1}.HFUNC(ZUnionStore) << 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 // GEO functions
<< CI{"GEOADD", CO::FAST | CO::WRITE | CO::DENYOOM, -5, 1, 1, 1}.HFUNC(GeoAdd) << 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}.HFUNC(GeoHash) << CI{"GEOHASH", CO::FAST | CO::READONLY, -2, 1, 1, 1, acl::kGeoHash}.HFUNC(GeoHash)
<< CI{"GEOPOS", CO::FAST | CO::READONLY, -2, 1, 1, 1}.HFUNC(GeoPos) << CI{"GEOPOS", CO::FAST | CO::READONLY, -2, 1, 1, 1, acl::kGeoPos}.HFUNC(GeoPos)
<< CI{"GEODIST", CO::READONLY, -4, 1, 1, 1}.HFUNC(GeoDist); << CI{"GEODIST", CO::READONLY, -4, 1, 1, 1, acl::kGeoDist}.HFUNC(GeoDist);
} }
} // namespace dfly } // namespace dfly