From 8d67486d50d7a183b129c62043b48890701e3b09 Mon Sep 17 00:00:00 2001 From: Qx Date: Wed, 10 Jan 2024 11:27:48 +0800 Subject: [PATCH] feat:pika support acl (#2013) copy from redis acl --------- Co-authored-by: Chengyu Liu --- conf/pika.conf | 34 +- include/acl.h | 433 +++++++++ include/pika_acl.h | 48 + include/pika_admin.h | 69 +- include/pika_bit.h | 22 +- include/pika_client_conn.h | 38 +- include/pika_cmd_table_manager.h | 9 + include/pika_command.h | 43 +- include/pika_conf.h | 58 +- include/pika_dispatch_thread.h | 2 + include/pika_geo.h | 29 +- include/pika_hash.h | 55 +- include/pika_kv.h | 109 ++- include/pika_list.h | 86 +- include/pika_pubsub.h | 52 +- include/pika_server.h | 49 +- include/pika_set.h | 55 +- include/pika_stream.h | 22 +- include/pika_transaction.h | 23 +- include/pika_zset.h | 62 +- src/acl.cc | 1368 ++++++++++++++++++++++++++++ src/net/include/net_pubsub.h | 6 + src/net/src/dispatch_thread.cc | 40 +- src/net/src/dispatch_thread.h | 2 + src/net/src/net_pubsub.cc | 118 ++- src/pika.cc | 8 +- src/pika_acl.cc | 323 +++++++ src/pika_admin.cc | 171 ++-- src/pika_client_conn.cc | 188 ++-- src/pika_cmd_table_manager.cc | 45 +- src/pika_command.cc | 399 ++++---- src/pika_conf.cc | 34 +- src/pika_dispatch_thread.cc | 15 + src/pika_pubsub.cc | 39 +- src/pika_server.cc | 219 +++-- src/pika_slot_command.cc | 29 - src/pstd/examples/hash_example.cc | 4 + src/pstd/include/base_conf.h | 7 +- src/pstd/include/pstd_hash.h | 2 + src/pstd/include/pstd_status.h | 11 +- src/pstd/include/pstd_string.h | 2 + src/pstd/src/base_conf.cc | 51 +- src/pstd/src/pstd_hash.cc | 11 + src/pstd/src/pstd_status.cc | 3 + src/pstd/src/pstd_string.cc | 4 + tests/assets/default.conf | 520 +++++++++-- tests/assets/nodefaultuser.acl | 2 + tests/assets/user.acl | 3 + tests/assets/userwithselectors.acl | 2 + tests/conf/pika.conf | 114 ++- tests/instances.tcl | 6 + tests/integration/server_test.go | 20 +- tests/support/test.tcl | 22 + tests/support/util.tcl | 22 + tests/test_helper.tcl | 3 +- tests/unit/acl.tcl | 1135 +++++++++++++++++++++++ 56 files changed, 5339 insertions(+), 907 deletions(-) create mode 100644 include/acl.h create mode 100644 include/pika_acl.h create mode 100644 src/acl.cc create mode 100644 src/pika_acl.cc create mode 100644 tests/assets/nodefaultuser.acl create mode 100644 tests/assets/user.acl create mode 100644 tests/assets/userwithselectors.acl create mode 100644 tests/unit/acl.tcl diff --git a/conf/pika.conf b/conf/pika.conf index c5bc276a30..ea2e411d04 100644 --- a/conf/pika.conf +++ b/conf/pika.conf @@ -68,12 +68,12 @@ requirepass : # [NOTICE] The value of this parameter must match the "requirepass" setting on the master. masterauth : -# The [password of user], which is empty by default. +# The [password of user], which is empty by default.(Deprecated) # [NOTICE] If this user password is the same as admin password (including both being empty), # the value of this parameter will be ignored and all users are considered as administrators, # in this scenario, users are not subject to the restrictions imposed by the userblacklist. # PS: "admin password" refers to value of the parameter above: requirepass. -userpass : +# userpass : # The blacklist of commands for users that logged in by userpass, # the commands that added to this list will not be available for users except for administrator. @@ -471,3 +471,33 @@ cache-maxmemory-samples: 5 # cache-lfu-decay-time cache-lfu-decay-time: 1 + +# is possible to manage access to Pub/Sub channels with ACL rules as well. The +# default Pub/Sub channels permission if new users is controlled by the +# acl-pubsub-default configuration directive, which accepts one of these values: +# +# allchannels: grants access to all Pub/Sub channels +# resetchannels: revokes access to all Pub/Sub channels +# +# acl-pubsub-default defaults to 'resetchannels' permission. +# acl-pubsub-default : resetchannels + +# ACL users are defined in the following format: +# user : ... acl rules ... +# +# For example: +# +# user : worker on >password ~key* +@all + +# Using an external ACL file +# +# Instead of configuring users here in this file, it is possible to use +# a stand-alone file just listing users. The two methods cannot be mixed: +# if you configure users here and at the same time you activate the external +# ACL file, the server will refuse to start. +# +# The format of the external ACL user file is exactly the same as the +# format that is used inside pika.conf to describe users. +# +# aclfile : ../conf/users.acl + diff --git a/include/acl.h b/include/acl.h new file mode 100644 index 0000000000..1363732352 --- /dev/null +++ b/include/acl.h @@ -0,0 +1,433 @@ +// Copyright (c) 2015-present, Qihoo, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. + +#ifndef PIKA_ACL_H +#define PIKA_ACL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pika_command.h" +#include "pstd_status.h" + +static const int USER_COMMAND_BITS_COUNT = 1024; + +enum class AclSelectorFlag { + ROOT = (1 << 0), // This is the root user permission selector + ALL_KEYS = (1 << 1), // The user can mention any key + ALL_COMMANDS = (1 << 2), // The user can run all commands + ALL_CHANNELS = (1 << 3), // The user can mention any Pub/Sub channel +}; + +enum class AclCategory { + 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), +}; + +enum class AclUserFlag { + ENABLED = (1 << 0), // The user is active + DISABLED = (1 << 1), // The user is disabled + NO_PASS = (1 << 2), /* The user requires no password, any provided password will work. For the + default user, this also means that no AUTH is needed, and every + connection is immediately authenticated. */ +}; + +enum class AclDeniedCmd { OK, CMD, KEY, CHANNEL, NUMBER, NO_SUB_CMD, NO_AUTH }; + +enum class AclLogCtx { + TOPLEVEL, + MULTI, + LUA, +}; + +// ACL key permission types +enum class AclPermission { + READ = (1 << 0), + WRITE = (1 << 1), + ALL = (READ | WRITE), +}; + +struct AclKeyPattern { + void ToString(std::string* str) { + if (flags & static_cast(AclPermission::ALL)) { + str->append("~"); + } else if (flags & static_cast(AclPermission::WRITE)) { + str->append("%W~"); + } else if (flags & static_cast(AclPermission::READ)) { + str->append("%R~"); + } + str->append(pattern); + } + + uint32_t flags; /* The CMD_KEYS_* flags for this key pattern */ + std::string pattern; /* The pattern to match keys against */ +}; + +class ACLLogEntry { + public: + ACLLogEntry() = delete; + ACLLogEntry(int32_t reason, int32_t context, const std::string& object, const std::string& username, int64_t ctime, + const std::string& cinfo) + : count_(1), + reason_(reason), + context_(context), + object_(object), + username_(username), + ctime_(ctime), + cinfo_(cinfo) {} + + bool Match(int32_t reason, int32_t context, int64_t ctime, const std::string& object, const std::string& username); + + void AddEntry(const std::string& cinfo, u_int64_t ctime); + + void GetReplyInfo(std::vector* vector); + + private: + uint64_t count_; + int32_t reason_; + int32_t context_; + std::string object_; + std::string username_; + int64_t ctime_; + std::string cinfo_; +}; + +class User; +class Acl; + +class AclSelector { + friend User; + + public: + explicit AclSelector() : AclSelector(0){}; + explicit AclSelector(uint32_t flag); + explicit AclSelector(const AclSelector& selector); + ~AclSelector() = default; + + inline uint32_t Flags() const { return flags_; }; + inline bool HasFlags(uint32_t flag) const { return flags_ & flag; }; + inline void AddFlags(uint32_t flag) { flags_ |= flag; }; + inline void DecFlags(uint32_t flag) { flags_ &= ~flag; }; + bool EqualChannel(const std::vector &allChannel); + + private: + pstd::Status SetSelector(const std::string& op); + + pstd::Status SetSelectorFromOpSet(const std::string& opSet); + + void ACLDescribeSelector(std::string* str); + + void ACLDescribeSelector(std::vector& vector); + + AclDeniedCmd CheckCanExecCmd(std::shared_ptr& cmd, int8_t subCmdIndex, const std::vector& keys, + std::string* errKey); + + bool SetSelectorCommandBitsForCategory(const std::string& categoryName, bool allow); + void SetAllCommandSelector(); + void RestAllCommandSelector(); + + void InsertKeyPattern(const std::string& str, uint32_t flags); + + void InsertChannel(const std::string& str); + + void ChangeSelector(const Cmd* cmd, bool allow); + void ChangeSelector(const std::shared_ptr& cmd, bool allow); + pstd::Status ChangeSelector(const std::shared_ptr& cmd, const std::string& subCmd, bool allow); + + void SetSubCommand(uint32_t cmdId); + void SetSubCommand(uint32_t cmdId, uint32_t subCmdIndex); + void ResetSubCommand(); + void ResetSubCommand(uint32_t cmdId); + void ResetSubCommand(uint32_t cmdId, uint32_t subCmdIndex); + + bool CheckSubCommand(uint32_t cmdId, uint32_t subCmdIndex); + + void DescribeSelectorCommandRules(std::string* str); + + // process acl command op, and sub command + pstd::Status SetCommandOp(const std::string& op, bool allow); + + // when modify command, do update Selector commandRule string + void UpdateCommonRule(const std::string& rule, bool allow); + + // remove rule string from Selector commandRule + void RemoveCommonRule(const std::string& rule); + + // clean commandRule + void CleanCommandRule(); + + bool CheckKey(const std::string& key, const uint32_t cmdFlag); + + bool CheckChannel(const std::string& key, bool isPattern); + + uint32_t flags_; // See SELECTOR_FLAG_* + + /* The bit in allowed_commands is set if this user has the right to + * execute this command.*/ + std::bitset allowedCommands_; + + // record subcommands,key is commandId,value subCommand bit index + std::map subCommand_; + + /* A list of allowed key patterns. If this field is empty the user cannot mention any key in a command, + * unless the flag ALLKEYS is set in the user. */ + std::list> patterns_; + + /* A list of allowed Pub/Sub channel patterns. If this field is empty the user cannot mention any + * channel in a `PUBLISH` or [P][UNSUBSCRIBE] command, unless the flag ALLCHANNELS is set in the user. */ + std::list channels_; + + /* A string representation of the ordered categories and commands, this + * is used to regenerate the original ACL string for display. + */ + std::string commandRules_; +}; + +// acl user +class User { + friend Acl; + + public: + User() = delete; + explicit User(std::string name); + explicit User(const User& user); + ~User() = default; + + std::string Name() const; + + // inline uint32_t Flags() const { return flags_; }; + inline bool HasFlags(uint32_t flag) const { return flags_ & flag; }; + inline void AddFlags(uint32_t flag) { flags_ |= flag; }; + inline void DecFlags(uint32_t flag) { flags_ &= ~flag; }; + + void CleanAclString(); + + /** + * store a password + * A lock is required before the call + * @param password + */ + void AddPassword(const std::string& password); + + /** + * delete a stored password + * A lock is required before the call + * @param password + */ + void RemovePassword(const std::string& password); + + // clean the user password + // A lock is required before the call + void CleanPassword(); + + // Add a selector to the user + // A lock is required before the call + void AddSelector(const std::shared_ptr& selector); + + // Set rule for user based on given parameters + // Use this function to handle it because it allows locking specified users + pstd::Status SetUser(const std::vector& rules); + + // Set the user rule with the given string + // A lock is required before the call + pstd::Status SetUser(const std::string& op); + + pstd::Status CreateSelectorFromOpSet(const std::string& opSet); + + // Get the user default selector + // A lock is required before the call + std::shared_ptr GetRootSelector(); + + void DescribeUser(std::string* str); + + // match the user password, when do auth, + // if match,return true, else return false + bool MatchPassword(const std::string& password); + + // handle Cmd Acl|get + void GetUserDescribe(CmdRes* res); + + // Get the user Channel key + // A lock is required before the call + std::vector AllChannelKey(); + + // check the user can exec the cmd + AclDeniedCmd CheckUserPermission(std::shared_ptr& cmd, const PikaCmdArgsType& argv, int8_t& subCmdIndex, + std::string* errKey); + + private: + mutable std::shared_mutex mutex_; + + const std::string name_; // The username + + std::atomic flags_ = static_cast(AclUserFlag::DISABLED); // See USER_FLAG_* + + std::set passwords_; // passwords for this user + + std::list> selectors_; /* A set of selectors this user validates commands + against. This list will always contain at least + one selector for backwards compatibility. */ + + std::string aclString_; /* cached string represent of ACLs */ +}; + +class Acl { + friend User; + friend AclSelector; + + public: + explicit Acl() = default; + ~Acl() = default; + + /** + * Initialization all acl + * @return + */ + pstd::Status Initialization(); + + /** + * create acl default user + * @return + */ + std::shared_ptr CreateDefaultUser(); + + std::shared_ptr CreatedUser(const std::string& name); + + /** + * Set user properties according to the string "op". + * @param op acl rule string + */ + pstd::Status SetUser(const std::string& userName, std::vector& op); + + /** + * get user from users_ map + * @param userName + * @return + */ + std::shared_ptr GetUser(const std::string& userName); + + std::shared_ptr GetUserLock(const std::string& userName); + + /** + * store a user to users_ map + * @param user + */ + void AddUser(const std::shared_ptr& user); + + void AddUserLock(const std::shared_ptr& user); + + // bo user auth, pass not is sha256 + std::shared_ptr Auth(const std::string& userName, const std::string& password); + + // get all user + std::vector Users(); + + void DescribeAllUser(std::vector* content); + + // save acl rule to file + pstd::Status SaveToFile(); + + // delete a user from users + std::set DeleteUser(const std::vector& userNames); + + // reload User from acl file, whe exec acl|load command + pstd::Status LoadUserFromFile(std::set* toUnAuthUsers); + + void UpdateDefaultUserPassword(const std::string& pass); + + // After the user channel is modified, determine whether the current channel needs to be disconnected + void KillPubsubClientsIfNeeded(const std::shared_ptr& origin, const std::shared_ptr& newUser); + + // check the user can be exec the command, after exec command + // bool CheckUserCanExec(const std::shared_ptr& cmd, const PikaCmdArgsType& argv); + + // Gets the value of the classification based on the cmd classification name + static uint32_t GetCommandCategoryFlagByName(const std::string& name); + + // Obtain the corresponding name based on category + static std::string GetCommandCategoryFlagByName(const uint32_t category); + + static std::vector GetAllCategoryName(); + + static const std::string DefaultUser; + static const int64_t LogGroupingMaxTimeDelta; + + // Adds a new entry in the ACL log, making sure to delete the old entry + // if we reach the maximum length allowed for the log. + void AddLogEntry(int32_t reason, int32_t context, const std::string& username, const std::string& object, + const std::string& cInfo); + + void GetLog(long count, CmdRes* res); + void ResetLog(); + + private: + /** + * This function is called once the server is already running,we are ready to start, + * in order to load the ACLs either from the pending list of users defined in redis.conf, + * or from the ACL file.The function will just exit with an error if the user is trying to mix + * both the loading methods. + */ + pstd::Status LoadUsersAtStartup(); + + /** + * Loads the ACL from the specified filename: every line + * is validated and should be either empty or in the format used to specify + * users in the pika.conf configuration or in the ACL file, that is: + * + * user ... rules ... + * + * @param users pika.conf users rule + */ + pstd::Status LoadUserConfigured(std::vector& users); + + /** + * Load ACL from acl rule file + * @param fileName file full name + */ + pstd::Status LoadUserFromFile(const std::string& fileName); + + void ACLMergeSelectorArguments(std::vector& argv, std::vector* merged); + mutable std::shared_mutex mutex_; + + static std::array, 21> CommandCategories; + + static std::array, 3> UserFlags; + + static std::array, 3> SelectorFlags; + + std::map> users_; + + std::list> logEntries_; +}; + +#endif // PIKA_ACL_H diff --git a/include/pika_acl.h b/include/pika_acl.h new file mode 100644 index 0000000000..cd2d942547 --- /dev/null +++ b/include/pika_acl.h @@ -0,0 +1,48 @@ +// Copyright (c) 2015-present, Qihoo, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. + +// pika ACL command +#ifndef PIKA_ACL_CMD_H +#define PIKA_ACL_CMD_H + +#include "include/pika_command.h" +#include "include/pika_server.h" + +extern PikaServer* g_pika_server; + +class PikaAclCmd : public Cmd { + public: + PikaAclCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) { + subCmdName_ = {"cat", "deluser", "dryrun", "genpass", "getuser", "list", "load", + "log", "save", "setuser", "users", "whoami", "help"}; + } + void Do(std::shared_ptr slot = nullptr) override; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; + Cmd* Clone() override { return new PikaAclCmd(*this); } + + private: + void DoInitial() override; + void Clear() override {} + + void Cat(); + void DelUser(); + void DryRun(); + void GenPass(); + void GetUser(); + void List(); + void Load(); + void Log(); + void Save(); + void SetUser(); + void Users(); + void WhoAmI(); + void Help(); + + std::string subCmd_; +}; + +#endif // PIKA_ACL_CMD_H diff --git a/include/pika_admin.h b/include/pika_admin.h index 7a9f0413d7..601040f4a5 100644 --- a/include/pika_admin.h +++ b/include/pika_admin.h @@ -15,9 +15,9 @@ #include #include -#include "storage/storage.h" - +#include "include/acl.h" #include "include/pika_command.h" +#include "storage/storage.h" #include "pika_db.h" /* @@ -25,7 +25,8 @@ */ class SlaveofCmd : public Cmd { public: - SlaveofCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SlaveofCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -45,7 +46,8 @@ class SlaveofCmd : public Cmd { class DbSlaveofCmd : public Cmd { public: - DbSlaveofCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DbSlaveofCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -76,13 +78,13 @@ class AuthCmd : public Cmd { Cmd* Clone() override { return new AuthCmd(*this); } private: - std::string pwd_; void DoInitial() override; }; class BgsaveCmd : public Cmd { public: - BgsaveCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + BgsaveCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -96,7 +98,8 @@ class BgsaveCmd : public Cmd { class CompactCmd : public Cmd { public: - CompactCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + CompactCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -137,7 +140,8 @@ class CompactRangeCmd : public Cmd { class PurgelogstoCmd : public Cmd { public: - PurgelogstoCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PurgelogstoCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -178,7 +182,8 @@ class SelectCmd : public Cmd { class FlushallCmd : public Cmd { public: - FlushallCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + FlushallCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} void Do(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; void DoUpdateCache(std::shared_ptr slot = nullptr) override; @@ -195,7 +200,10 @@ class FlushallCmd : public Cmd { class FlushdbCmd : public Cmd { public: - FlushdbCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + FlushdbCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} + // The flush command belongs to the write categories, so the key cannot be empty + std::vector current_key() const override { return {""}; } void Do(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; void DoUpdateCache(std::shared_ptr slot = nullptr) override; @@ -215,7 +223,9 @@ class FlushdbCmd : public Cmd { class ClientCmd : public Cmd { public: - ClientCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ClientCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + subCmdName_ = {"getname", "setname", "list", "addr", "kill"}; + } void Do(std::shared_ptr slot = nullptr) override; const static std::string CLIENT_LIST_S; const static std::string CLIENT_KILL_S; @@ -305,7 +315,8 @@ class InfoCmd : public Cmd { class ShutdownCmd : public Cmd { public: - ShutdownCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ShutdownCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -317,7 +328,10 @@ class ShutdownCmd : public Cmd { class ConfigCmd : public Cmd { public: - ConfigCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ConfigCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) { + subCmdName_ = {"get", "set", "rewrite", "resetstat"}; + } void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -336,7 +350,8 @@ class ConfigCmd : public Cmd { class MonitorCmd : public Cmd { public: - MonitorCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + MonitorCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -348,7 +363,8 @@ class MonitorCmd : public Cmd { class DbsizeCmd : public Cmd { public: - DbsizeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DbsizeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -384,7 +400,8 @@ class LastsaveCmd : public Cmd { class DelbackupCmd : public Cmd { public: - DelbackupCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DelbackupCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -397,7 +414,7 @@ class DelbackupCmd : public Cmd { class EchoCmd : public Cmd { public: EchoCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} - void Merge() override {}; + void Merge() override{}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; Cmd* Clone() override { return new EchoCmd(*this); } @@ -424,7 +441,8 @@ class ScandbCmd : public Cmd { class SlowlogCmd : public Cmd { public: enum SlowlogCondition { kGET, kLEN, kRESET }; - SlowlogCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SlowlogCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -442,7 +460,8 @@ class SlowlogCmd : public Cmd { class PaddingCmd : public Cmd { public: - PaddingCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PaddingCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -455,7 +474,8 @@ class PaddingCmd : public Cmd { class PKPatternMatchDelCmd : public Cmd { public: - PKPatternMatchDelCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PKPatternMatchDelCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -470,7 +490,8 @@ class PKPatternMatchDelCmd : public Cmd { class DummyCmd : public Cmd { public: DummyCmd() : Cmd("", 0, 0) {} - DummyCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DummyCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -506,7 +527,8 @@ class HelloCmd : public Cmd { class DiskRecoveryCmd : public Cmd { public: - DiskRecoveryCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DiskRecoveryCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -519,7 +541,8 @@ class DiskRecoveryCmd : public Cmd { class ClearReplicationIDCmd : public Cmd { public: - ClearReplicationIDCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ClearReplicationIDCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::ADMIN)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; diff --git a/include/pika_bit.h b/include/pika_bit.h index 573cc78f9d..fc3c29a226 100644 --- a/include/pika_bit.h +++ b/include/pika_bit.h @@ -8,16 +8,18 @@ #include "storage/storage.h" +#include "include/acl.h" #include "include/pika_command.h" -#include "include/pika_slot.h" #include "include/pika_kv.h" +#include "include/pika_slot.h" /* * bitoperation */ class BitGetCmd : public Cmd { public: - BitGetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + BitGetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::BITMAP)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -44,7 +46,8 @@ class BitGetCmd : public Cmd { class BitSetCmd : public Cmd { public: - BitSetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + BitSetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::BITMAP)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -72,7 +75,8 @@ class BitSetCmd : public Cmd { class BitCountCmd : public Cmd { public: - BitCountCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + BitCountCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::BITMAP)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -103,7 +107,8 @@ class BitCountCmd : public Cmd { class BitPosCmd : public Cmd { public: - BitPosCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + BitPosCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::BITMAP)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -138,7 +143,8 @@ class BitPosCmd : public Cmd { class BitOpCmd : public Cmd { public: - BitOpCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + BitOpCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::BITMAP)) { set_cmd_ = std::make_shared(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv); }; BitOpCmd(const BitOpCmd& other) @@ -150,9 +156,7 @@ class BitOpCmd : public Cmd { set_cmd_ = std::make_shared(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv); } - std::vector current_key() const override { - return {dest_key_}; - } + std::vector current_key() const override { return {dest_key_}; } void Do(std::shared_ptr slot = nullptr) override; void DoUpdateCache(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; diff --git a/include/pika_client_conn.h b/include/pika_client_conn.h index 05333dd024..89e5c121b4 100644 --- a/include/pika_client_conn.h +++ b/include/pika_client_conn.h @@ -9,6 +9,7 @@ #include #include +#include "acl.h" #include "include/pika_command.h" @@ -63,28 +64,11 @@ class PikaClientConn : public net::RedisConn { static constexpr uint8_t Execing = 3; }; - // Auth related - class AuthStat { - public: - void Init(); - bool IsAuthed(const std::shared_ptr& cmd_ptr); - bool ChecknUpdate(const std::string& message); - - private: - enum StatType { - kNoAuthed = 0, - kAdminAuthed, - kLimitAuthed, - }; - StatType stat_; - }; - PikaClientConn(int fd, const std::string& ip_port, net::Thread* server_thread, net::NetMultiplexer* mpx, const net::HandleType& handle_type, int max_conn_rbuf_size); ~PikaClientConn() = default; - void ProcessRedisCmds(const std::vector& argvs, bool async, - std::string* response) override; + void ProcessRedisCmds(const std::vector& argvs, bool async, std::string* response) override; void BatchExecRedisCmd(const std::vector& argvs); int DealMessage(const net::RedisCmdArgsType& argv, std::string* response) override { return 0; } @@ -97,6 +81,16 @@ class PikaClientConn : public net::RedisConn { const std::string& GetCurrentTable() override { return current_db_; } void SetWriteCompleteCallback(WriteCompleteCallback cb) { write_completed_cb_ = std::move(cb); } + void DoAuth(const std::shared_ptr& user); + + void UnAuth(const std::shared_ptr& user); + + bool IsAuthed() const; + + bool AuthRequired() const; + + std::string UserName() const; + // Txn void PushCmdToQue(std::shared_ptr cmd); std::queue> GetTxnCmdQue(); @@ -118,8 +112,7 @@ class PikaClientConn : public net::RedisConn { void ExitTxn(); net::ServerThread* server_thread() { return server_thread_; } - - AuthStat& auth_stat() { return auth_stat_; } + void ClientInfoToString(std::string* info, const std::string& cmdName); std::atomic resp_num; std::vector> resp_array; @@ -135,6 +128,9 @@ class PikaClientConn : public net::RedisConn { std::unordered_set watched_db_keys_; std::mutex txn_state_mu_; + bool authenticated_ = false; + std::shared_ptr user_; + std::shared_ptr DoCmd(const PikaCmdArgsType& argv, const std::string& opt, const std::shared_ptr& resp_ptr); @@ -143,8 +139,6 @@ class PikaClientConn : public net::RedisConn { void ExecRedisCmd(const PikaCmdArgsType& argv, std::shared_ptr& resp_ptr); void TryWriteResp(); - - AuthStat auth_stat_; }; struct ClientInfo { diff --git a/include/pika_cmd_table_manager.h b/include/pika_cmd_table_manager.h index af408fb14b..184c0ee0a8 100644 --- a/include/pika_cmd_table_manager.h +++ b/include/pika_cmd_table_manager.h @@ -9,6 +9,7 @@ #include #include +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_data_distribution.h" @@ -23,13 +24,19 @@ struct CommandStatistics { }; class PikaCmdTableManager { + friend AclSelector; + public: PikaCmdTableManager(); virtual ~PikaCmdTableManager() = default; + void InitCmdTable(void); std::shared_ptr GetCmd(const std::string& opt); uint32_t DistributeKey(const std::string& key, uint32_t slot_num); bool CmdExist(const std::string& cmd) const; CmdTable* GetCmdTable(); + uint32_t GetCmdId(); + + std::vector GetAclCategoryCmdNames(uint32_t flag); /* * Info Commandstats used @@ -44,6 +51,8 @@ class PikaCmdTableManager { std::unique_ptr cmds_; + uint32_t cmdId_ = 0; + std::shared_mutex map_protector_; std::unordered_map> thread_distribution_map_; diff --git a/include/pika_command.h b/include/pika_command.h index df93b7c6ec..c7250c870e 100644 --- a/include/pika_command.h +++ b/include/pika_command.h @@ -231,6 +231,9 @@ const std::string kCmdNamePubSub = "pubsub"; const std::string kCmdNamePSubscribe = "psubscribe"; const std::string kCmdNamePUnSubscribe = "punsubscribe"; +// ACL +const std::string KCmdNameAcl = "acl"; + // Stream const std::string kCmdNameXAdd = "xadd"; const std::string kCmdNameXDel = "xdel"; @@ -241,7 +244,6 @@ const std::string kCmdNameXRevrange = "xrevrange"; const std::string kCmdNameXTrim = "xtrim"; const std::string kCmdNameXInfo = "xinfo"; - const std::string kClusterPrefix = "pkcluster"; using PikaCmdArgsType = net::RedisCmdArgsType; static const int RAW_ARGS_LEN = 1024 * 1024; @@ -271,6 +273,8 @@ enum CmdFlags { kCmdFlagsOperateKey = (1 << 21), // redis keySpace kCmdFlagsPreDo = (1 << 22), kCmdFlagsStream = (1 << 23), + kCmdFlagsFast = (1 << 24), + kCmdFlagsSlow = (1 << 25), }; void inline RedisAppendContent(std::string& str, const std::string& value); @@ -424,6 +428,18 @@ class CmdRes { AppendContent(value); } void AppendStringRaw(const std::string& value) { message_.append(value); } + + void AppendStringVector(const std::vector& strArray) { + if (strArray.empty()) { + AppendArrayLen(-1); + return; + } + AppendArrayLen(strArray.size()); + for (const auto& item : strArray) { + AppendString(item); + } + } + void SetRes(CmdRet _ret, const std::string& content = "") { ret_ = _ret; if (!content.empty()) { @@ -470,7 +486,17 @@ class Cmd : public std::enable_shared_from_this { std::shared_ptr sync_slot; HintKeys hint_keys; }; - Cmd(std::string name, int arity, uint32_t flag) : name_(std::move(name)), arity_(arity), flag_(flag) {} + struct CommandStatistics { + CommandStatistics() = default; + CommandStatistics(const CommandStatistics& other) { + cmd_time_consuming.store(other.cmd_time_consuming.load()); + cmd_count.store(other.cmd_count.load()); + } + std::atomic cmd_count = {0}; + std::atomic cmd_time_consuming = {0}; + }; + CommandStatistics state; + Cmd(std::string name, int arity, uint32_t flag, uint32_t aclCategory = 0); virtual ~Cmd() = default; virtual std::vector current_key() const; @@ -487,6 +513,8 @@ class Cmd : public std::enable_shared_from_this { virtual void Split(std::shared_ptr slot, const HintKeys& hint_keys) = 0; virtual void Merge() = 0; + int8_t SubCmdIndex(const std::string& cmdName); // if the command no subCommand,return -1; + void Initial(const PikaCmdArgsType& argv, const std::string& db_name); uint32_t flag() const; @@ -499,12 +527,16 @@ class Cmd : public std::enable_shared_from_this { bool IsAdminRequire() const; bool is_single_slot() const; bool is_multi_slot() const; + bool HasSubCommand() const; // The command is there a sub command + std::vector SubCommand() const; // Get command is there a sub command bool IsNeedUpdateCache() const; bool is_only_from_cache() const; bool IsNeedReadCache() const; bool IsNeedCacheDo() const; bool HashtagIsConsistent(const std::string& lhs, const std::string& rhs) const; uint64_t GetDoDuration() const { return do_duration_; }; + uint32_t AclCategory() const; + void AddAclCategory(uint32_t aclCategory); void SetDbName(const std::string& db_name) { db_name_ = db_name; } std::string GetDBName() { return db_name_; } @@ -525,6 +557,9 @@ class Cmd : public std::enable_shared_from_this { virtual void DoBinlog(const std::shared_ptr& slot); + uint32_t GetCmdId() const { return cmdId_; }; + bool CheckArg(uint64_t num) const; + protected: // enable copy, used default copy // Cmd(const Cmd&); @@ -533,13 +568,13 @@ class Cmd : public std::enable_shared_from_this { void InternalProcessCommand(const std::shared_ptr& slot, const std::shared_ptr& sync_slot, const HintKeys& hint_key); void DoCommand(const std::shared_ptr& slot, const HintKeys& hint_key); - bool CheckArg(uint64_t num) const; void LogCommand() const; std::string name_; int arity_ = -2; uint32_t flag_ = 0; + std::vector subCmdName_; // sub command name, may be empty protected: CmdRes res_; @@ -551,6 +586,8 @@ class Cmd : public std::enable_shared_from_this { std::weak_ptr resp_; CmdStage stage_ = kNone; uint64_t do_duration_ = 0; + uint32_t cmdId_ = 0; + uint32_t aclCategory_ = 0; private: virtual void DoInitial() = 0; diff --git a/include/pika_conf.h b/include/pika_conf.h index 3e2d0a2a5e..4dbc07b5fd 100644 --- a/include/pika_conf.h +++ b/include/pika_conf.h @@ -15,6 +15,7 @@ #include "pstd/include/pstd_mutex.h" #include "pstd/include/pstd_string.h" +#include "acl.h" #include "include/pika_define.h" #include "include/pika_meta.h" #include "rocksdb/compression_type.h" @@ -175,18 +176,6 @@ class PikaConf : public pstd::BaseConf { std::shared_lock l(rwlock_); return bgsave_prefix_; } - std::string userpass() { - std::shared_lock l(rwlock_); - return userpass_; - } - std::string suser_blacklist() { - std::shared_lock l(rwlock_); - return pstd::StringConcat(user_blacklist_, COMMA); - } - const std::vector& vuser_blacklist() { - std::shared_lock l(rwlock_); - return user_blacklist_; - } bool classic_mode() { return classic_mode_.load(); } int databases() { std::shared_lock l(rwlock_); @@ -376,6 +365,12 @@ class PikaConf : public pstd::BaseConf { std::string compression_all_levels() const { return compression_per_level_; }; static rocksdb::CompressionType GetCompression(const std::string& value); + std::vector& users() { return users_; }; + std::string acl_file() { return aclFile_; }; + + uint32_t acl_pubsub_default() { return acl_pubsub_default_.load(); } + uint32_t acl_log_max_len() { return acl_Log_max_len_.load(); } + // Setter void SetPort(const int value) { std::lock_guard l(rwlock_); @@ -466,20 +461,7 @@ class PikaConf : public pstd::BaseConf { TryPushDiffCommands("masterauth", value); masterauth_ = value; } - void SetUserPass(const std::string& value) { - std::lock_guard l(rwlock_); - TryPushDiffCommands("userpass", value); - userpass_ = value; - } - void SetUserBlackList(const std::string& value) { - std::lock_guard l(rwlock_); - TryPushDiffCommands("userblacklist", value); - pstd::StringSplit(value, COMMA, user_blacklist_); - for (auto& item : user_blacklist_) { - pstd::StringToLower(item); - } - } - void SetSlotMigrate(const std::string &value) { + void SetSlotMigrate(const std::string& value) { std::lock_guard l(rwlock_); slotmigrate_ = (value == "yes") ? true : false; } @@ -610,6 +592,20 @@ class PikaConf : public pstd::BaseConf { TryPushDiffCommands("max-rsync-parallel-num", std::to_string(value)); max_rsync_parallel_num_ = value; } + void SetAclPubsubDefault(const std::string& value) { + std::lock_guard l(rwlock_); + TryPushDiffCommands("acl-pubsub-default", value); + if (value == "resetchannels") { + acl_pubsub_default_ = 0; + } else { + acl_pubsub_default_ = static_cast(AclSelectorFlag::ALL_CHANNELS); + } + } + void SetAclLogMaxLen(const int value) { + std::lock_guard l(rwlock_); + TryPushDiffCommands("acllog-max-len", std::to_string(value)); + acl_Log_max_len_ = value; + } void SetCacheType(const std::string &value); void SetCacheDisableFlag() { tmp_cache_disable_flag_ = true; } @@ -631,6 +627,7 @@ class PikaConf : public pstd::BaseConf { int Load(); int ConfigRewrite(); int ConfigRewriteReplicationID(); + private: pstd::Status InternalGetTargetDB(const std::string& db_name, uint32_t* target); @@ -666,8 +663,6 @@ class PikaConf : public pstd::BaseConf { std::string replication_id_; std::string requirepass_; std::string masterauth_; - std::string userpass_; - std::vector user_blacklist_; std::atomic classic_mode_; int databases_ = 0; int default_slot_num_ = 0; @@ -719,6 +714,13 @@ class PikaConf : public pstd::BaseConf { std::string network_interface_; + std::vector users_; // acl user rules + + std::string aclFile_; + + std::atomic acl_pubsub_default_ = 0; // default channel pub/sub permission + std::atomic acl_Log_max_len_ = 0; // default acl log max len + // diff commands between cached commands and config file commands std::map diff_commands_; void TryPushDiffCommands(const std::string& command, const std::string& value); diff --git a/include/pika_dispatch_thread.h b/include/pika_dispatch_thread.h index ac9f5a519c..89dbb79333 100644 --- a/include/pika_dispatch_thread.h +++ b/include/pika_dispatch_thread.h @@ -22,6 +22,8 @@ class PikaDispatchThread { void SetQueueLimit(int queue_limit) { thread_rep_->SetQueueLimit(queue_limit); } + void UnAuthUserAndKillClient(const std::set &users, const std::shared_ptr& defaultUser); + private: class ClientConnFactory : public net::ConnFactory { public: diff --git a/include/pika_geo.h b/include/pika_geo.h index 76624038b3..9a077e9ff8 100644 --- a/include/pika_geo.h +++ b/include/pika_geo.h @@ -6,6 +6,7 @@ #ifndef PIKA_GEO_H_ #define PIKA_GEO_H_ +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" @@ -50,7 +51,8 @@ struct GeoRange { class GeoAddCmd : public Cmd { public: - GeoAddCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoAddCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -69,15 +71,16 @@ class GeoAddCmd : public Cmd { class GeoPosCmd : public Cmd { public: - GeoPosCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoPosCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); return res; } void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new GeoPosCmd(*this); } private: @@ -88,15 +91,16 @@ class GeoPosCmd : public Cmd { class GeoDistCmd : public Cmd { public: - GeoDistCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoDistCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); return res; } void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new GeoDistCmd(*this); } private: @@ -106,7 +110,8 @@ class GeoDistCmd : public Cmd { class GeoHashCmd : public Cmd { public: - GeoHashCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoHashCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -114,7 +119,7 @@ class GeoHashCmd : public Cmd { } void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Merge() override{}; Cmd* Clone() override { return new GeoHashCmd(*this); } private: @@ -125,7 +130,8 @@ class GeoHashCmd : public Cmd { class GeoRadiusCmd : public Cmd { public: - GeoRadiusCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoRadiusCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -150,7 +156,8 @@ class GeoRadiusCmd : public Cmd { class GeoRadiusByMemberCmd : public Cmd { public: - GeoRadiusByMemberCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GeoRadiusByMemberCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::GEO)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; diff --git a/include/pika_hash.h b/include/pika_hash.h index 4cecd6f14a..40d5d1c5fb 100644 --- a/include/pika_hash.h +++ b/include/pika_hash.h @@ -8,6 +8,7 @@ #include "storage/storage.h" +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" @@ -16,7 +17,8 @@ */ class HDelCmd : public Cmd { public: - HDelCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HDelCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -39,7 +41,8 @@ class HDelCmd : public Cmd { class HGetCmd : public Cmd { public: - HGetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HGetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -61,7 +64,8 @@ class HGetCmd : public Cmd { class HGetallCmd : public Cmd { public: - HGetallCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HGetallCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -83,7 +87,8 @@ class HGetallCmd : public Cmd { class HSetCmd : public Cmd { public: - HSetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HSetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -104,7 +109,8 @@ class HSetCmd : public Cmd { class HExistsCmd : public Cmd { public: - HExistsCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HExistsCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -126,7 +132,8 @@ class HExistsCmd : public Cmd { class HIncrbyCmd : public Cmd { public: - HIncrbyCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HIncrbyCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -148,7 +155,8 @@ class HIncrbyCmd : public Cmd { class HIncrbyfloatCmd : public Cmd { public: - HIncrbyfloatCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HIncrbyfloatCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -169,7 +177,8 @@ class HIncrbyfloatCmd : public Cmd { class HKeysCmd : public Cmd { public: - HKeysCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HKeysCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -191,7 +200,8 @@ class HKeysCmd : public Cmd { class HLenCmd : public Cmd { public: - HLenCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HLenCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -213,7 +223,8 @@ class HLenCmd : public Cmd { class HMgetCmd : public Cmd { public: - HMgetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HMgetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -236,7 +247,8 @@ class HMgetCmd : public Cmd { class HMsetCmd : public Cmd { public: - HMsetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HMsetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -258,7 +270,8 @@ class HMsetCmd : public Cmd { class HSetnxCmd : public Cmd { public: - HSetnxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HSetnxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -279,7 +292,8 @@ class HSetnxCmd : public Cmd { class HStrlenCmd : public Cmd { public: - HStrlenCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HStrlenCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -301,7 +315,8 @@ class HStrlenCmd : public Cmd { class HValsCmd : public Cmd { public: - HValsCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + HValsCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -323,7 +338,8 @@ class HValsCmd : public Cmd { class HScanCmd : public Cmd { public: - HScanCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + HScanCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -335,7 +351,7 @@ class HScanCmd : public Cmd { Cmd* Clone() override { return new HScanCmd(*this); } private: - std::string key_; + std::string key_; std::string pattern_; int64_t cursor_; int64_t count_{10}; @@ -348,7 +364,8 @@ class HScanCmd : public Cmd { class HScanxCmd : public Cmd { public: - HScanxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + HScanxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -374,7 +391,7 @@ class HScanxCmd : public Cmd { class PKHScanRangeCmd : public Cmd { public: PKHScanRangeCmd(const std::string& name, int arity, uint32_t flag) - : Cmd(name, arity, flag), pattern_("*") {} + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -401,7 +418,7 @@ class PKHScanRangeCmd : public Cmd { class PKHRScanRangeCmd : public Cmd { public: PKHRScanRangeCmd(const std::string& name, int arity, uint32_t flag) - : Cmd(name, arity, flag), pattern_("*") {} + : Cmd(name, arity, flag, static_cast(AclCategory::HASH)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); diff --git a/include/pika_kv.h b/include/pika_kv.h index 1c06b425e6..977c469bb8 100644 --- a/include/pika_kv.h +++ b/include/pika_kv.h @@ -8,6 +8,7 @@ #include "storage/storage.h" +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" @@ -17,7 +18,8 @@ class SetCmd : public Cmd { public: enum SetCondition { kNONE, kNX, kXX, kVX, kEXORPX }; - SetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {}; + SetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -49,7 +51,8 @@ class SetCmd : public Cmd { class GetCmd : public Cmd { public: - GetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + GetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -73,7 +76,8 @@ class GetCmd : public Cmd { class DelCmd : public Cmd { public: - DelCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + DelCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)){}; void Do(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; void DoUpdateCache(std::shared_ptr slot = nullptr) override; @@ -92,7 +96,8 @@ class DelCmd : public Cmd { class IncrCmd : public Cmd { public: - IncrCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + IncrCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -114,7 +119,8 @@ class IncrCmd : public Cmd { class IncrbyCmd : public Cmd { public: - IncrbyCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + IncrbyCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -136,7 +142,8 @@ class IncrbyCmd : public Cmd { class IncrbyfloatCmd : public Cmd { public: - IncrbyfloatCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + IncrbyfloatCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -158,7 +165,8 @@ class IncrbyfloatCmd : public Cmd { class DecrCmd : public Cmd { public: - DecrCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + DecrCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -180,7 +188,8 @@ class DecrCmd : public Cmd { class DecrbyCmd : public Cmd { public: - DecrbyCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + DecrbyCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -202,7 +211,8 @@ class DecrbyCmd : public Cmd { class GetsetCmd : public Cmd { public: - GetsetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + GetsetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -224,7 +234,8 @@ class GetsetCmd : public Cmd { class AppendCmd : public Cmd { public: - AppendCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + AppendCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -246,7 +257,8 @@ class AppendCmd : public Cmd { class MgetCmd : public Cmd { public: - MgetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + MgetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)){}; void Do(std::shared_ptr slot = nullptr) override; void ReadCache(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; @@ -269,7 +281,8 @@ class MgetCmd : public Cmd { class KeysCmd : public Cmd { public: - KeysCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + KeysCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -285,7 +298,8 @@ class KeysCmd : public Cmd { class SetnxCmd : public Cmd { public: - SetnxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SetnxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -307,7 +321,8 @@ class SetnxCmd : public Cmd { class SetexCmd : public Cmd { public: - SetexCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SetexCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -331,7 +346,8 @@ class SetexCmd : public Cmd { class PsetexCmd : public Cmd { public: - PsetexCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PsetexCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -355,7 +371,8 @@ class PsetexCmd : public Cmd { class DelvxCmd : public Cmd { public: - DelvxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DelvxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -376,7 +393,8 @@ class DelvxCmd : public Cmd { class MsetCmd : public Cmd { public: - MsetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + MsetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) { set_cmd_ = std::make_shared(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv); } MsetCmd(const MsetCmd& other) : Cmd(other), kvs_(other.kvs_) { @@ -408,7 +426,8 @@ class MsetCmd : public Cmd { class MsetnxCmd : public Cmd { public: - MsetnxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + MsetnxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) { set_cmd_ = std::make_shared(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv); } MsetnxCmd(const MsetnxCmd& other) @@ -438,7 +457,8 @@ class MsetnxCmd : public Cmd { class GetrangeCmd : public Cmd { public: - GetrangeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + GetrangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -464,7 +484,8 @@ class GetrangeCmd : public Cmd { class SetrangeCmd : public Cmd { public: - SetrangeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SetrangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -487,7 +508,8 @@ class SetrangeCmd : public Cmd { class StrlenCmd : public Cmd { public: - StrlenCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + StrlenCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STRING)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -511,7 +533,8 @@ class StrlenCmd : public Cmd { class ExistsCmd : public Cmd { public: - ExistsCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ExistsCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} void Do(std::shared_ptr slot = nullptr) override; void ReadCache(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; @@ -528,7 +551,8 @@ class ExistsCmd : public Cmd { class ExpireCmd : public Cmd { public: - ExpireCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ExpireCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -551,7 +575,8 @@ class ExpireCmd : public Cmd { class PexpireCmd : public Cmd { public: - PexpireCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PexpireCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -574,7 +599,8 @@ class PexpireCmd : public Cmd { class ExpireatCmd : public Cmd { public: - ExpireatCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ExpireatCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -596,7 +622,8 @@ class ExpireatCmd : public Cmd { class PexpireatCmd : public Cmd { public: - PexpireatCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PexpireatCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -619,7 +646,8 @@ class PexpireatCmd : public Cmd { class TtlCmd : public Cmd { public: - TtlCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + TtlCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -640,7 +668,8 @@ class TtlCmd : public Cmd { class PttlCmd : public Cmd { public: - PttlCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PttlCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -661,7 +690,8 @@ class PttlCmd : public Cmd { class PersistCmd : public Cmd { public: - PersistCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PersistCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -682,7 +712,8 @@ class PersistCmd : public Cmd { class TypeCmd : public Cmd { public: - TypeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + TypeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -703,7 +734,8 @@ class TypeCmd : public Cmd { class PTypeCmd : public Cmd { public: - PTypeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PTypeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -722,7 +754,8 @@ class PTypeCmd : public Cmd { class ScanCmd : public Cmd { public: - ScanCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + ScanCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)), pattern_("*") {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -744,7 +777,8 @@ class ScanCmd : public Cmd { class ScanxCmd : public Cmd { public: - ScanxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + ScanxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)), pattern_("*") {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -765,7 +799,8 @@ class ScanxCmd : public Cmd { class PKSetexAtCmd : public Cmd { public: - PKSetexAtCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PKSetexAtCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -787,7 +822,8 @@ class PKSetexAtCmd : public Cmd { class PKScanRangeCmd : public Cmd { public: - PKScanRangeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + PKScanRangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_start_); @@ -816,7 +852,8 @@ class PKScanRangeCmd : public Cmd { class PKRScanRangeCmd : public Cmd { public: - PKRScanRangeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + PKRScanRangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::KEYSPACE)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_start_); diff --git a/include/pika_list.h b/include/pika_list.h index 68c7c2c74d..d7ac1752de 100644 --- a/include/pika_list.h +++ b/include/pika_list.h @@ -6,6 +6,7 @@ #ifndef PIKA_LIST_H_ #define PIKA_LIST_H_ +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" #include "storage/storage.h" @@ -15,7 +16,8 @@ */ class LIndexCmd : public Cmd { public: - LIndexCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LIndexCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -39,7 +41,8 @@ class LIndexCmd : public Cmd { class LInsertCmd : public Cmd { public: - LInsertCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {}; + LInsertCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -63,7 +66,8 @@ class LInsertCmd : public Cmd { class LLenCmd : public Cmd { public: - LLenCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LLenCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -85,37 +89,37 @@ class LLenCmd : public Cmd { class BlockingBaseCmd : public Cmd { public: - BlockingBaseCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + BlockingBaseCmd(const std::string& name, int arity, uint32_t flag, uint32_t category = 0) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST) | category) {} - //blpop/brpop used start - struct WriteBinlogOfPopArgs{ + // blpop/brpop used start + struct WriteBinlogOfPopArgs { BlockKeyType block_type; std::string key; std::shared_ptr slot; std::shared_ptr conn; WriteBinlogOfPopArgs() = default; - WriteBinlogOfPopArgs(BlockKeyType block_type_, const std::string& key_, - std::shared_ptr slot_, std::shared_ptr conn_) - : block_type(block_type_), key(key_), slot(slot_), conn(conn_){} + WriteBinlogOfPopArgs(BlockKeyType block_type_, const std::string& key_, std::shared_ptr slot_, + std::shared_ptr conn_) + : block_type(block_type_), key(key_), slot(slot_), conn(conn_) {} }; void BlockThisClientToWaitLRPush(BlockKeyType block_pop_type, std::vector& keys, int64_t expire_time); void TryToServeBLrPopWithThisKey(const std::string& key, std::shared_ptr slot); static void ServeAndUnblockConns(void* args); static void WriteBinlogOfPop(std::vector& pop_args); - void removeDuplicates(std::vector & keys_); - //blpop/brpop used functions end + void removeDuplicates(std::vector& keys_); + // blpop/brpop used functions end }; class BLPopCmd final : public BlockingBaseCmd { public: - BLPopCmd(const std::string& name, int arity, uint32_t flag) : BlockingBaseCmd(name, arity, flag){}; - virtual std::vector current_key() const override { - return { keys_ }; - } - virtual void Do(std::shared_ptr slot = nullptr) override; - virtual void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - virtual void Merge() override {}; - virtual Cmd* Clone() override { return new BLPopCmd(*this); } + BLPopCmd(const std::string& name, int arity, uint32_t flag) + : BlockingBaseCmd(name, arity, flag, static_cast(AclCategory::BLOCKING)){}; + std::vector current_key() const override { return {keys_}; } + void Do(std::shared_ptr slot = nullptr) override; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; + Cmd* Clone() override { return new BLPopCmd(*this); } void DoInitial() override; void DoBinlog(const std::shared_ptr& slot) override; @@ -129,7 +133,8 @@ class BLPopCmd final : public BlockingBaseCmd { class LPopCmd : public Cmd { public: - LPopCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LPopCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -149,7 +154,6 @@ class LPopCmd : public Cmd { rocksdb::Status s_; }; - class LPushCmd : public BlockingBaseCmd { public: LPushCmd(const std::string& name, int arity, uint32_t flag) : BlockingBaseCmd(name, arity, flag){}; @@ -175,7 +179,8 @@ class LPushCmd : public BlockingBaseCmd { class LPushxCmd : public Cmd { public: - LPushxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LPushxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -198,7 +203,8 @@ class LPushxCmd : public Cmd { class LRangeCmd : public Cmd { public: - LRangeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LRangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -222,7 +228,8 @@ class LRangeCmd : public Cmd { class LRemCmd : public Cmd { public: - LRemCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LRemCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -245,7 +252,8 @@ class LRemCmd : public Cmd { class LSetCmd : public Cmd { public: - LSetCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LSetCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -268,7 +276,8 @@ class LSetCmd : public Cmd { class LTrimCmd : public Cmd { public: - LTrimCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + LTrimCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -291,16 +300,16 @@ class LTrimCmd : public Cmd { class BRPopCmd final : public BlockingBaseCmd { public: - BRPopCmd(const std::string& name, int arity, uint32_t flag) : BlockingBaseCmd(name, arity, flag){}; - virtual std::vector current_key() const override { - return { keys_ }; - } - virtual void Do(std::shared_ptr slot = nullptr) override; - virtual void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - virtual void Merge() override {}; - virtual Cmd* Clone() override { return new BRPopCmd(*this); } + BRPopCmd(const std::string& name, int arity, uint32_t flag) + : BlockingBaseCmd(name, arity, flag, static_cast(AclCategory::BLOCKING)){}; + std::vector current_key() const override { return {keys_}; } + void Do(std::shared_ptr slot = nullptr) override; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; + Cmd* Clone() override { return new BRPopCmd(*this); } void DoInitial() override; void DoBinlog(const std::shared_ptr& slot) override; + private: std::vector keys_; int64_t expire_time_{0}; @@ -310,7 +319,8 @@ class BRPopCmd final : public BlockingBaseCmd { class RPopCmd : public Cmd { public: - RPopCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + RPopCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -332,7 +342,8 @@ class RPopCmd : public Cmd { class RPopLPushCmd : public BlockingBaseCmd { public: - RPopLPushCmd(const std::string& name, int arity, uint32_t flag) : BlockingBaseCmd(name, arity, flag) { + RPopLPushCmd(const std::string& name, int arity, uint32_t flag) + : BlockingBaseCmd(name, arity, flag, static_cast(AclCategory::BLOCKING)) { rpop_cmd_ = std::make_shared(kCmdNameRPop, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList); lpush_cmd_ = std::make_shared(kCmdNameLPush, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList); }; @@ -396,7 +407,8 @@ class RPushCmd : public BlockingBaseCmd { class RPushxCmd : public Cmd { public: - RPushxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; + RPushxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::LIST)){}; std::vector current_key() const override { std::vector res; res.push_back(key_); diff --git a/include/pika_pubsub.h b/include/pika_pubsub.h index c42cfc4c66..d440e02bf4 100644 --- a/include/pika_pubsub.h +++ b/include/pika_pubsub.h @@ -6,6 +6,7 @@ #ifndef PIKA_PUBSUB_H_ #define PIKA_PUBSUB_H_ +#include "acl.h" #include "pika_command.h" /* @@ -13,11 +14,13 @@ */ class PublishCmd : public Cmd { public: - PublishCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PublishCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new PublishCmd(*this); } + std::vector current_key() const override { return {channel_}; } private: std::string channel_; @@ -27,58 +30,71 @@ class PublishCmd : public Cmd { class SubscribeCmd : public Cmd { public: - SubscribeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SubscribeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new SubscribeCmd(*this); } + std::vector current_key() const override { return channels_; } private: + std::vector channels_; void DoInitial() override; }; class UnSubscribeCmd : public Cmd { public: - UnSubscribeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + UnSubscribeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new UnSubscribeCmd(*this); } + std::vector current_key() const override { return channels_; } private: + std::vector channels_; void DoInitial() override; }; class PUnSubscribeCmd : public Cmd { public: - PUnSubscribeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PUnSubscribeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new PUnSubscribeCmd(*this); } + std::vector current_key() const override { return {channels_}; } private: + std::vector channels_; void DoInitial() override; }; class PSubscribeCmd : public Cmd { public: - PSubscribeCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PSubscribeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new PSubscribeCmd(*this); } + std::vector current_key() const override { return {channels_}; } + std::vector channels_; private: void DoInitial() override; }; class PubSubCmd : public Cmd { public: - PubSubCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + PubSubCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::PUBSUB)) {} void Do(std::shared_ptr slot = nullptr) override; - void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; - void Merge() override {}; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; + void Merge() override{}; Cmd* Clone() override { return new PubSubCmd(*this); } private: diff --git a/include/pika_server.h b/include/pika_server.h index 69592522c7..d6c450fc5d 100644 --- a/include/pika_server.h +++ b/include/pika_server.h @@ -26,27 +26,29 @@ #include "storage/backupable.h" #include "storage/storage.h" +#include "acl.h" #include "include/pika_auxiliary_thread.h" #include "include/pika_binlog.h" #include "include/pika_client_processor.h" +#include "include/pika_cmd_table_manager.h" #include "include/pika_conf.h" #include "include/pika_db.h" #include "include/pika_define.h" #include "include/pika_dispatch_thread.h" #include "include/pika_instant.h" +#include "include/pika_migrate_thread.h" #include "include/pika_repl_client.h" #include "include/pika_repl_server.h" #include "include/pika_rsync_service.h" -#include "include/pika_migrate_thread.h" -#include "include/rsync_server.h" -#include "include/pika_statistic.h" #include "include/pika_slot_command.h" +#include "include/pika_statistic.h" #include "include/pika_transaction.h" #include "include/pika_cmd_table_manager.h" #include "include/pika_cache.h" #include "include/pika_slot.h" +#include "include/rsync_server.h" /* static std::set MultiKvCommands {kCmdNameDel, @@ -171,7 +173,7 @@ class PikaServer : public pstd::noncopyable { int role(); bool leader_protected_mode(); void CheckLeaderProtectedMode(); - bool readonly(const std::string& table, const std::string& key); + bool readonly(const std::string& table); int repl_state(); std::string repl_state_str(); bool force_full_sync(); @@ -293,7 +295,8 @@ class PikaServer : public pstd::noncopyable { * DBSync used */ pstd::Status GetDumpUUID(const std::string& db_name, const uint32_t slot_id, std::string* snapshot_uuid); - pstd::Status GetDumpMeta(const std::string& db_name, const uint32_t slot_id, std::vector* files, std::string* snapshot_uuid); + pstd::Status GetDumpMeta(const std::string& db_name, const uint32_t slot_id, std::vector* files, + std::string* snapshot_uuid); void DBSync(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id); void TryDBSync(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id, int32_t top); void DbSyncSendFile(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id); @@ -315,6 +318,7 @@ class PikaServer : public pstd::noncopyable { * Monitor used */ bool HasMonitorClients() const; + bool ClientIsMonitor(const std::shared_ptr& client_ptr) const; void AddMonitorMessage(const std::string& monitor_message); void AddMonitorClient(const std::shared_ptr& client_ptr); @@ -374,6 +378,9 @@ class PikaServer : public pstd::noncopyable { void PubSubChannels(const std::string& pattern, std::vector* result); void PubSubNumSub(const std::vector& channels, std::vector>* result); + int ClientPubSubChannelSize(const std::shared_ptr& conn); + int ClientPubSubChannelPatternSize(const std::shared_ptr& conn); + pstd::Status GetCmdRouting(std::vector& redis_cmds, std::vector* dst, bool* all_local); // info debug use @@ -382,16 +389,18 @@ class PikaServer : public pstd::noncopyable { /* * * Async migrate used */ - int SlotsMigrateOne(const std::string &key, const std::shared_ptr &slot); - bool SlotsMigrateBatch(const std::string &ip, int64_t port, int64_t time_out, int64_t slots, int64_t keys_num, const std::shared_ptr &slot); - void GetSlotsMgrtSenderStatus(std::string *ip, int64_t *port, int64_t *slot, bool *migrating, int64_t *moved, int64_t *remained); + int SlotsMigrateOne(const std::string& key, const std::shared_ptr& slot); + bool SlotsMigrateBatch(const std::string& ip, int64_t port, int64_t time_out, int64_t slots, int64_t keys_num, + const std::shared_ptr& slot); + void GetSlotsMgrtSenderStatus(std::string* ip, int64_t* port, int64_t* slot, bool* migrating, int64_t* moved, + int64_t* remained); bool SlotsMigrateAsyncCancel(); std::shared_mutex bgsave_protector_; BgSaveInfo bgsave_info_; /* - * BGSlotsReload used + * BGSlotsReload used */ struct BGSlotsReload { bool reloading = false; @@ -440,6 +449,11 @@ class PikaServer : public pstd::noncopyable { } void Bgslotsreload(const std::shared_ptr& slot); + // Revoke the authorization of the specified account, when handle Cmd deleteUser + void AllClientUnAuth(const std::set& users); + + // Determine whether the user's conn can continue to subscribe to the channel + void CheckPubsubClientKill(const std::string& userName, const std::vector& allChannel); /* * BGSlotsCleanup used @@ -470,7 +484,6 @@ class PikaServer : public pstd::noncopyable { BGSlotsCleanup bgslots_cleanup_; net::BGThread bgslots_cleanup_thread_; - BGSlotsCleanup bgslots_cleanup() { std::lock_guard ml(bgsave_protector_); return bgslots_cleanup_; @@ -529,6 +542,13 @@ class PikaServer : public pstd::noncopyable { return dbs_; } + /* + * acl init + */ + pstd::Status InitAcl() { return acl_->Initialization(); } + + std::unique_ptr<::Acl>& Acl() { return acl_; } + friend class Cmd; friend class InfoCmd; friend class PikaReplClientConn; @@ -578,7 +598,7 @@ class PikaServer : public pstd::noncopyable { void AutoUpdateNetworkMetric(); void PrintThreadPoolQueueStatus(); int64_t GetLastSaveTime(const std::string& dump_dir); - + std::string host_; int port_ = 0; time_t start_time_s_ = 0; @@ -588,7 +608,7 @@ class PikaServer : public pstd::noncopyable { void InitStorageOptions(); std::atomic exit_; - std::timed_mutex exit_mutex_; + std::timed_mutex exit_mutex_; /* * Table used @@ -702,6 +722,11 @@ class PikaServer : public pstd::noncopyable { * lastsave used */ int64_t lastsave_ = 0; + + /* + * acl + */ + std::unique_ptr<::Acl> acl_ = nullptr; }; #endif diff --git a/include/pika_set.h b/include/pika_set.h index e7ee45e790..9a84d3e8c9 100644 --- a/include/pika_set.h +++ b/include/pika_set.h @@ -6,6 +6,7 @@ #ifndef PIKA_SET_H_ #define PIKA_SET_H_ +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" #include "pika_kv.h" @@ -15,7 +16,8 @@ */ class SAddCmd : public Cmd { public: - SAddCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SAddCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -37,7 +39,8 @@ class SAddCmd : public Cmd { class SPopCmd : public Cmd { public: - SPopCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SPopCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -60,7 +63,8 @@ class SPopCmd : public Cmd { class SCardCmd : public Cmd { public: - SCardCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SCardCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -82,7 +86,8 @@ class SCardCmd : public Cmd { class SMembersCmd : public Cmd { public: - SMembersCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SMembersCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -104,7 +109,8 @@ class SMembersCmd : public Cmd { class SScanCmd : public Cmd { public: - SScanCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + SScanCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -128,7 +134,8 @@ class SScanCmd : public Cmd { class SRemCmd : public Cmd { public: - SRemCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SRemCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -151,7 +158,8 @@ class SRemCmd : public Cmd { class SUnionCmd : public Cmd { public: - SUnionCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SUnionCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -162,28 +170,26 @@ class SUnionCmd : public Cmd { void DoInitial() override; }; - -class SetOperationCmd : public Cmd{ +class SetOperationCmd : public Cmd { public: - SetOperationCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + SetOperationCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) { sadd_cmd_ = std::make_shared(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); del_cmd_ = std::make_shared(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv); } SetOperationCmd(const SetOperationCmd& other) - : Cmd(other), dest_key_(other.dest_key_), value_to_dest_(other.value_to_dest_){ + : Cmd(other), dest_key_(other.dest_key_), value_to_dest_(other.value_to_dest_) { sadd_cmd_ = std::make_shared(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); del_cmd_ = std::make_shared(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv); } - std::vector current_key() const override { - return {dest_key_}; - } + std::vector current_key() const override { return {dest_key_}; } void DoBinlog(const std::shared_ptr& slot) override; protected: std::string dest_key_; std::vector keys_; - //used for write binlog + // used for write binlog std::shared_ptr sadd_cmd_; std::shared_ptr del_cmd_; std::vector value_to_dest_; @@ -207,7 +213,8 @@ class SUnionstoreCmd : public SetOperationCmd { class SInterCmd : public Cmd { public: - SInterCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SInterCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -235,7 +242,8 @@ class SInterstoreCmd : public SetOperationCmd { class SIsmemberCmd : public Cmd { public: - SIsmemberCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SIsmemberCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -258,7 +266,8 @@ class SIsmemberCmd : public Cmd { class SDiffCmd : public Cmd { public: - SDiffCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SDiffCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; void Merge() override {}; @@ -286,7 +295,8 @@ class SDiffstoreCmd : public SetOperationCmd { class SMoveCmd : public Cmd { public: - SMoveCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) { + SMoveCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) { srem_cmd_ = std::make_shared(kCmdNameSRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); sadd_cmd_ = std::make_shared(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); } @@ -299,9 +309,7 @@ class SMoveCmd : public Cmd { srem_cmd_ = std::make_shared(kCmdNameSRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); sadd_cmd_ = std::make_shared(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet); } - std::vector current_key() const override { - return {src_key_, dest_key_}; - } + std::vector current_key() const override { return {src_key_, dest_key_}; } void Do(std::shared_ptr slot = nullptr) override; void DoUpdateCache(std::shared_ptr slot = nullptr) override; void DoThroughDB(std::shared_ptr slot = nullptr) override; @@ -321,7 +329,8 @@ class SMoveCmd : public Cmd { class SRandmemberCmd : public Cmd { public: - SRandmemberCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + SRandmemberCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); diff --git a/include/pika_stream.h b/include/pika_stream.h index f7834243e6..da539c0d5f 100644 --- a/include/pika_stream.h +++ b/include/pika_stream.h @@ -30,7 +30,8 @@ inline void AppendMessagesToRes(CmdRes& res, std::vector& f class XAddCmd : public Cmd { public: - XAddCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XAddCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; std::vector current_key() const override { return {key_}; } void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; @@ -48,7 +49,8 @@ class XAddCmd : public Cmd { class XDelCmd : public Cmd { public: - XDelCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XDelCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; std::vector current_key() const override { return {key_}; } void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; @@ -68,7 +70,8 @@ class XDelCmd : public Cmd { class XReadCmd : public Cmd { public: - XReadCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XReadCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; void Merge() override{}; @@ -86,7 +89,8 @@ class XReadCmd : public Cmd { class XRangeCmd : public Cmd { public: - XRangeCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XRangeCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; void Merge() override{}; @@ -105,7 +109,7 @@ class XRangeCmd : public Cmd { class XRevrangeCmd : public XRangeCmd { public: - XRevrangeCmd(const std::string& name, int arity, uint16_t flag) : XRangeCmd(name, arity, flag){}; + XRevrangeCmd(const std::string& name, int arity, uint32_t flag) : XRangeCmd(name, arity, flag){}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; void Merge() override{}; @@ -114,7 +118,8 @@ class XRevrangeCmd : public XRangeCmd { class XLenCmd : public Cmd { public: - XLenCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XLenCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; void Merge() override{}; @@ -128,7 +133,7 @@ class XLenCmd : public Cmd { class XTrimCmd : public Cmd { public: - XTrimCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XTrimCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag){}; std::vector current_key() const override { return {key_}; } void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; @@ -144,7 +149,8 @@ class XTrimCmd : public Cmd { class XInfoCmd : public Cmd { public: - XInfoCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag){}; + XInfoCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::STREAM)){}; void Do(std::shared_ptr slot = nullptr) override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override{}; void Merge() override{}; diff --git a/include/pika_transaction.h b/include/pika_transaction.h index f47e9e8424..dd25fb12a8 100644 --- a/include/pika_transaction.h +++ b/include/pika_transaction.h @@ -6,6 +6,7 @@ #ifndef PIKA_TRANSACTION_H_ #define PIKA_TRANSACTION_H_ +#include "acl.h" #include "include/pika_command.h" #include "net/include/redis_conn.h" #include "pika_db.h" @@ -13,7 +14,8 @@ class MultiCmd : public Cmd { public: - MultiCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + MultiCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::TRANSACTION)) {} void Do(std::shared_ptr slot = nullptr) override; Cmd* Clone() override { return new MultiCmd(*this); } void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {} @@ -25,18 +27,21 @@ class MultiCmd : public Cmd { class ExecCmd : public Cmd { public: - ExecCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ExecCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::TRANSACTION)) {} void Do(std::shared_ptr slot = nullptr) override; Cmd* Clone() override { return new ExecCmd(*this); } void Execute() override; void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {} void Merge() override {} std::vector current_key() const override { return {}; } + private: struct CmdInfo { public: CmdInfo(std::shared_ptr cmd, std::shared_ptr db, std::shared_ptr slot, - std::shared_ptr sync_slot) : cmd_(cmd), db_(db), slot_(slot), sync_slot_(sync_slot) {} + std::shared_ptr sync_slot) + : cmd_(cmd), db_(db), slot_(slot), sync_slot_(sync_slot) {} std::shared_ptr cmd_; std::shared_ptr db_; std::shared_ptr slot_; @@ -59,18 +64,21 @@ class ExecCmd : public Cmd { class DiscardCmd : public Cmd { public: - DiscardCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + DiscardCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::TRANSACTION)) {} void Do(std::shared_ptr slot = nullptr) override; Cmd* Clone() override { return new DiscardCmd(*this); } void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {} void Merge() override {} + private: void DoInitial() override; }; class WatchCmd : public Cmd { public: - WatchCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + WatchCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::TRANSACTION)) {} void Do(std::shared_ptr slot = nullptr) override; void Execute() override; @@ -78,6 +86,7 @@ class WatchCmd : public Cmd { Cmd* Clone() override { return new WatchCmd(*this); } void Merge() override {} std::vector current_key() const override { return keys_; } + private: void DoInitial() override; std::vector keys_; @@ -86,12 +95,14 @@ class WatchCmd : public Cmd { class UnwatchCmd : public Cmd { public: - UnwatchCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + UnwatchCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::TRANSACTION)) {} void Do(std::shared_ptr slot = nullptr) override; Cmd* Clone() override { return new UnwatchCmd(*this); } void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {} void Merge() override {} + private: void DoInitial() override; }; diff --git a/include/pika_zset.h b/include/pika_zset.h index 2ce98ddc87..00a1b5bc28 100644 --- a/include/pika_zset.h +++ b/include/pika_zset.h @@ -8,6 +8,7 @@ #include "storage/storage.h" +#include "include/acl.h" #include "include/pika_command.h" #include "include/pika_slot.h" #include "pika_kv.h" @@ -17,7 +18,8 @@ */ class ZAddCmd : public Cmd { public: - ZAddCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZAddCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -39,7 +41,8 @@ class ZAddCmd : public Cmd { class ZCardCmd : public Cmd { public: - ZCardCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZCardCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -60,7 +63,8 @@ class ZCardCmd : public Cmd { class ZScanCmd : public Cmd { public: - ZScanCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag), pattern_("*") {} + ZScanCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)), pattern_("*") {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -83,7 +87,8 @@ class ZScanCmd : public Cmd { class ZIncrbyCmd : public Cmd { public: - ZIncrbyCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZIncrbyCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -106,7 +111,8 @@ class ZIncrbyCmd : public Cmd { class ZsetRangeParentCmd : public Cmd { public: - ZsetRangeParentCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZsetRangeParentCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} protected: std::string key_; @@ -161,7 +167,8 @@ class ZRevrangeCmd : public ZsetRangeParentCmd { class ZsetRangebyscoreParentCmd : public Cmd { public: - ZsetRangebyscoreParentCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZsetRangebyscoreParentCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} double MinScore() { return min_score_; } double MaxScore() { return max_score_; } @@ -229,7 +236,8 @@ class ZRevrangebyscoreCmd : public ZsetRangebyscoreParentCmd { class ZCountCmd : public Cmd { public: - ZCountCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZCountCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -262,7 +270,8 @@ class ZCountCmd : public Cmd { class ZRemCmd : public Cmd { public: - ZRemCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZRemCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -286,7 +295,7 @@ class ZRemCmd : public Cmd { class ZsetUIstoreParentCmd : public Cmd { public: ZsetUIstoreParentCmd(const std::string& name, int arity, uint32_t flag) - : Cmd(name, arity, flag) { + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) { zadd_cmd_ = std::make_unique(kCmdNameZAdd, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset); del_cmd_ = std::make_shared(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv); } @@ -301,9 +310,8 @@ class ZsetUIstoreParentCmd : public Cmd { del_cmd_ = std::make_shared(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv); } - std::vector current_key() const override { - return {dest_key_}; - } + std::vector current_key() const override { return {dest_key_}; } + protected: std::string dest_key_; int64_t num_keys_ = 0; @@ -312,7 +320,7 @@ class ZsetUIstoreParentCmd : public Cmd { std::vector weights_; void DoInitial() override; void Clear() override { aggregate_ = storage::SUM; } - //used for write binlog + // used for write binlog std::shared_ptr zadd_cmd_; std::shared_ptr del_cmd_; }; @@ -329,7 +337,7 @@ class ZUnionstoreCmd : public ZsetUIstoreParentCmd { private: void DoInitial() override; - //used for write binlog + // used for write binlog std::map value_to_dest_; rocksdb::Status s_; void DoBinlog(const std::shared_ptr& slot) override; @@ -349,13 +357,14 @@ class ZInterstoreCmd : public ZsetUIstoreParentCmd { private: void DoInitial() override; rocksdb::Status s_; - //used for write binlog + // used for write binlog std::vector value_to_dest_; }; class ZsetRankParentCmd : public Cmd { public: - ZsetRankParentCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZsetRankParentCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} protected: std::string key_, member_; @@ -428,7 +437,8 @@ class ZScoreCmd : public ZsetRankParentCmd { class ZsetRangebylexParentCmd : public Cmd { public: - ZsetRangebylexParentCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZsetRangebylexParentCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} protected: std::string key_, min_member_, max_member_; @@ -487,7 +497,8 @@ class ZRevrangebylexCmd : public ZsetRangebylexParentCmd { class ZLexcountCmd : public Cmd { public: - ZLexcountCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZLexcountCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -512,7 +523,8 @@ class ZLexcountCmd : public Cmd { class ZRemrangebyrankCmd : public Cmd { public: - ZRemrangebyrankCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZRemrangebyrankCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -535,7 +547,8 @@ class ZRemrangebyrankCmd : public Cmd { class ZRemrangebyscoreCmd : public Cmd { public: - ZRemrangebyscoreCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZRemrangebyscoreCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -559,7 +572,8 @@ class ZRemrangebyscoreCmd : public Cmd { class ZRemrangebylexCmd : public Cmd { public: - ZRemrangebylexCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZRemrangebylexCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); @@ -583,7 +597,8 @@ class ZRemrangebylexCmd : public Cmd { class ZPopmaxCmd : public Cmd { public: - ZPopmaxCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZPopmaxCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.emplace_back(key_); @@ -602,7 +617,8 @@ class ZPopmaxCmd : public Cmd { class ZPopminCmd : public Cmd { public: - ZPopminCmd(const std::string& name, int arity, uint32_t flag) : Cmd(name, arity, flag) {} + ZPopminCmd(const std::string& name, int arity, uint32_t flag) + : Cmd(name, arity, flag, static_cast(AclCategory::SORTEDSET)) {} std::vector current_key() const override { std::vector res; res.push_back(key_); diff --git a/src/acl.cc b/src/acl.cc new file mode 100644 index 0000000000..50a71fb072 --- /dev/null +++ b/src/acl.cc @@ -0,0 +1,1368 @@ +// Copyright (c) 2015-present, Qihoo, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. + +#include +#include +#include + +#include "include/acl.h" +#include "include/pika_cmd_table_manager.h" +#include "include/pika_server.h" +#include "pstd_hash.h" + +extern PikaServer* g_pika_server; + +extern std::unique_ptr g_pika_cmd_table_manager; + +// class User +User::User(std::string name) : name_(std::move(name)) { + selectors_.emplace_back(std::make_shared(static_cast(AclSelectorFlag::ROOT))); +} + +User::User(const User& user) : name_(user.Name()) { + flags_ = user.flags_.load(); + passwords_ = user.passwords_; + aclString_ = user.aclString_; + for (const auto& item : user.selectors_) { + selectors_.emplace_back(std::make_shared(*item)); + } +} + +std::string User::Name() const { return name_; } + +void User::CleanAclString() { aclString_.clear(); } + +void User::AddPassword(const std::string& password) { passwords_.insert(password); } + +void User::RemovePassword(const std::string& password) { passwords_.erase(password); } + +void User::CleanPassword() { passwords_.clear(); } + +void User::AddSelector(const std::shared_ptr& selector) { selectors_.push_back(selector); } + +pstd::Status User::SetUser(const std::vector& rules) { + std::unique_lock wl(mutex_); + + for (const auto& rule : rules) { + auto status = SetUser(rule); + if (!status.ok()) { + LOG(ERROR) << "SetUser rule:" << rule << status.ToString(); + return status; + } + } + + return pstd::Status::OK(); +} + +pstd::Status User::SetUser(const std::string& op) { + CleanAclString(); + if (op.empty()) { + return pstd::Status::OK(); + } + if (!strcasecmp(op.data(), "on")) { + AddFlags(static_cast(AclUserFlag::ENABLED)); + DecFlags(static_cast(AclUserFlag::DISABLED)); + } else if (!strcasecmp(op.data(), "off")) { + AddFlags(static_cast(AclUserFlag::DISABLED)); + DecFlags(static_cast(AclUserFlag::ENABLED)); + } else if (!strcasecmp(op.data(), "nopass")) { + AddFlags(static_cast(AclUserFlag::NO_PASS)); + CleanPassword(); + } else if (!strcasecmp(op.data(), "resetpass")) { + DecFlags(static_cast(AclUserFlag::NO_PASS)); + CleanPassword(); + } else if (op[0] == '>' || op[0] == '#') { + std::string newpass; + if (op[0] == '>') { + newpass = pstd::sha256(op.data() + 1); + } else { + if (!pstd::isSha256(op.data() + 1)) { + return pstd::Status::Error("password not sha256"); + } + newpass = op.data() + 1; + } + AddPassword(newpass); + DecFlags(static_cast(AclUserFlag::NO_PASS)); + } else if (op[0] == '<' || op[0] == '!') { + std::string delpass; + if (op[0] == '<') { + delpass = pstd::sha256(op.data() + 1); + } else { + if (!pstd::isSha256(op.data() + 1)) { + return pstd::Status::Error("password not sha256"); + } + delpass = op.data() + 1; + } + // passwords_.erase(delpass); + RemovePassword(delpass); + } else if (op[0] == '(' && op[op.size() - 1] == ')') { + auto status = CreateSelectorFromOpSet(op); + if (!status.ok()) { + return status; + } + } else if (!strcasecmp(op.data(), "clearselectors")) { + selectors_.clear(); + return pstd::Status::OK(); + } else if (!strcasecmp(op.data(), "reset")) { + auto status = SetUser("resetpass"); + if (!status.ok()) { + return status; + } + status = SetUser("resetkeys"); + if (!status.ok()) { + return status; + } + status = SetUser("resetchannels"); + if (!status.ok()) { + return status; + } + if (g_pika_conf->acl_pubsub_default() & static_cast(AclSelectorFlag::ALL_CHANNELS)) { + status = SetUser("allchannels"); + if (!status.ok()) { + return status; + } + } + status = SetUser("off"); + if (!status.ok()) { + return status; + } + status = SetUser("-@all"); + if (!status.ok()) { + return status; + } + } else { + auto root = GetRootSelector(); + if (!root) { // does not appear under normal circumstances + LOG(ERROR) << "set user:" << Name() << " not find root selector"; + return pstd::Status::Error("set user error,See pika log for details"); + } + auto status = root->SetSelector(op); + if (!status.ok()) { + return status; + } + } + + return pstd::Status::OK(); +} + +pstd::Status User::CreateSelectorFromOpSet(const std::string& opSet) { + auto selector = std::make_shared(); + auto status = selector->SetSelectorFromOpSet(opSet); + if (!status.ok()) { + return status; + } + AddSelector(selector); + return status; +} + +std::shared_ptr User::GetRootSelector() { + for (const auto& item : selectors_) { + if (item->HasFlags(static_cast(AclSelectorFlag::ROOT))) { + return item; + } + } + return nullptr; +} + +void User::DescribeUser(std::string* str) { + std::unique_lock wl(mutex_); + + if (!aclString_.empty()) { + str->append(aclString_); + return; + } + + // flag + for (const auto& item : Acl::UserFlags) { + if (HasFlags(item.second)) { + aclString_ += " "; + aclString_ += item.first; + } + } + + // password + for (const auto& item : passwords_) { + aclString_ += " #" + item; + } + + // selector + std::string selectorStr; + for (const auto& item : selectors_) { + selectorStr.clear(); + item->ACLDescribeSelector(&selectorStr); + + if (item->HasFlags(static_cast(AclSelectorFlag::ROOT))) { + aclString_ += selectorStr; + } else { + aclString_ += fmt::format(" ({})", selectorStr.data() + 1); + } + } + + str->append(aclString_); +} + +bool User::MatchPassword(const std::string& password) { + std::shared_lock l(mutex_); + return passwords_.find(password) != passwords_.end(); +} + +void User::GetUserDescribe(CmdRes* res) { + std::shared_lock l(mutex_); + + res->AppendArrayLen(12); + + res->AppendString("flags"); + std::vector vector; + for (const auto& item : Acl::UserFlags) { + if (HasFlags(item.second)) { + vector.emplace_back(item.first); + } + } + res->AppendStringVector(vector); + + vector.clear(); + res->AppendString("passwords"); + for (const auto& item : passwords_) { + vector.emplace_back(item); + } + res->AppendStringVector(vector); + + size_t i = 0; + for (const auto& selector : selectors_) { + vector.clear(); + if (i == 0) { // root selector + selector->ACLDescribeSelector(vector); + for (const auto& item : vector) { + res->AppendString(item); + } + + res->AppendString("selectors"); + if (selectors_.size() == 1) { + res->AppendArrayLen(0); + } + ++i; + continue; + } + if (i == 1) { + res->AppendArrayLen(static_cast(selectors_.size()) - 1); + } + selector->ACLDescribeSelector(vector); + res->AppendStringVector(vector); + ++i; + } +} + +AclDeniedCmd User::CheckUserPermission(std::shared_ptr& cmd, const PikaCmdArgsType& argv, int8_t& subCmdIndex, + std::string* errKey) { + std::shared_lock l(mutex_); + + subCmdIndex = -1; + if (cmd->HasSubCommand()) { + subCmdIndex = cmd->SubCmdIndex(argv[1]); + if (subCmdIndex < 0) { + return AclDeniedCmd::NO_SUB_CMD; + } + } + auto keys = cmd->current_key(); + AclDeniedCmd res = AclDeniedCmd::OK; + for (const auto& selector : selectors_) { + res = selector->CheckCanExecCmd(cmd, subCmdIndex, keys, errKey); + if (res == AclDeniedCmd::OK) { + return AclDeniedCmd::OK; + } + } + return res; +} + +std::vector User::AllChannelKey() { + std::vector result; + for (const auto& selector : selectors_) { + for (const auto& item : selector->channels_) { + result.emplace_back(item); + } + } + return result; +} +// class User end + +// class Acl +pstd::Status Acl::Initialization() { + AddUser(CreateDefaultUser()); + UpdateDefaultUserPassword(g_pika_conf->requirepass()); + auto status = LoadUsersAtStartup(); + if (!status.ok()) { + return status; + } + return status; +} + +std::shared_ptr Acl::GetUser(const std::string& userName) { + auto u = users_.find(userName); + if (u == users_.end()) { + return nullptr; + } + return u->second; +} + +std::shared_ptr Acl::GetUserLock(const std::string& userName) { + std::shared_lock rl(mutex_); + auto u = users_.find(userName); + if (u == users_.end()) { + return nullptr; + } + return u->second; +} + +void Acl::AddUser(const std::shared_ptr& user) { users_[user->Name()] = user; } + +void Acl::AddUserLock(const std::shared_ptr& user) { + std::unique_lock wl(mutex_); + users_[user->Name()] = user; +} + +pstd::Status Acl::LoadUsersAtStartup() { + if (!g_pika_conf->users().empty() && !g_pika_conf->acl_file().empty()) { + return pstd::Status::NotSupported("Only one configuration file and acl file can be used", ""); + } + + if (g_pika_conf->users().empty()) { + return LoadUserFromFile(g_pika_conf->acl_file()); + } else { + return LoadUserConfigured(g_pika_conf->users()); + } +} + +pstd::Status Acl::LoadUserConfigured(std::vector& users) { + std::vector userRules; + for (const auto& item : users) { + userRules.clear(); + pstd::StringSplit(item, ' ', userRules); + if (userRules.size() < 2) { + return pstd::Status::Error("acl from configuration file read rules error"); + } + auto user = GetUser(userRules[0]); + if (user) { + if (user->Name() != DefaultUser) { // only `default` users are allowed to repeat + return pstd::Status::Error("acl user: " + user->Name() + " is repeated"); + } else { + user->SetUser("reset"); + } + } else { + user = CreatedUser(userRules[0]); + } + std::vector aclArgc; + auto subRule = std::vector(userRules.begin() + 1, userRules.end()); + ACLMergeSelectorArguments(subRule, &aclArgc); + + for (const auto& rule : aclArgc) { + auto status = user->SetUser(rule); + if (!status.ok()) { + LOG(ERROR) << "load user from configured file error," << status.ToString(); + return status; + } + } + AddUser(user); + } + + return pstd::Status().OK(); +} + +pstd::Status Acl::LoadUserFromFile(std::set* toUnAuthUsers) { + for (const auto& item : users_) { + if (item.first != DefaultUser) { + toUnAuthUsers->insert(item.first); + } + } + + auto status = LoadUserFromFile(g_pika_conf->acl_file()); + if (!status.ok()) { + return status; + } + + return status; +} + +pstd::Status Acl::LoadUserFromFile(const std::string& fileName) { + if (fileName.empty()) { + return pstd::Status::OK(); + } + + std::unique_lock wl(mutex_); + + std::unique_ptr sequentialFile; + auto status = NewSequentialFile(fileName, sequentialFile); + if (!status.ok()) { + return status; + } + + std::map> users; + std::vector rules; + const int lineLength = 1024 * 1024; + char line[lineLength]; + + bool hasDefaultUser = false; + + int lineNum = 0; + while (sequentialFile->ReadLine(line, lineLength) != nullptr) { + ++lineNum; + size_t lineLen = strlen(line); + if (lineLen == 0) { + continue; + } + + std::string lineContent = pstd::StringTrim(line, "\r\n "); + rules.clear(); + pstd::StringSplit(lineContent, ' ', rules); + if (rules.empty()) { + continue; + } + + if (rules[0] != "user" || rules.size() < 2) { + LOG(ERROR) << fmt::format("load user from acl file,line:{} '{}' illegal", lineNum, lineContent); + return pstd::Status::Error(fmt::format("line:{} '{}' illegal", lineNum, lineContent)); + } + + auto user = users.find(rules[1]); + if (user != users.end()) { + // if user is exists, exit + auto err = fmt::format("Duplicate user '{}' found on line {}.", rules[1], lineNum); + LOG(ERROR) << err; + return pstd::Status::Error(err); + } + + std::vector aclArgc; + auto subRule = std::vector(rules.begin() + 2, rules.end()); + ACLMergeSelectorArguments(subRule, &aclArgc); + + auto u = CreatedUser(rules[1]); + for (const auto& item : aclArgc) { + status = u->SetUser(item); + if (!status.ok()) { + LOG(ERROR) << "load user from acl file error," << status.ToString(); + return status; + } + } + if (rules[1] == DefaultUser) { + hasDefaultUser = true; + } + users[rules[1]] = u; + } + + if (!hasDefaultUser) { + users[DefaultUser] = GetUser(DefaultUser); + } + + users_ = std::move(users); + + return pstd::Status().OK(); +} + +void Acl::UpdateDefaultUserPassword(const std::string& pass) { + std::unique_lock wl(mutex_); + auto u = GetUser(DefaultUser); + u->SetUser("resetpass"); + if (pass.empty()) { + u->SetUser("nopass"); + } else { + u->SetUser(">" + pass); + } +} + +// bool Acl::CheckUserCanExec(const std::shared_ptr& cmd, const PikaCmdArgsType& argv) { cmd->name(); } + +std::shared_ptr Acl::CreateDefaultUser() { + auto defaultUser = std::make_shared(DefaultUser); + defaultUser->SetUser("+@all"); + defaultUser->SetUser("~*"); + defaultUser->SetUser("&*"); + defaultUser->SetUser("on"); + defaultUser->SetUser("nopass"); + return defaultUser; +} + +std::shared_ptr Acl::CreatedUser(const std::string& name) { return std::make_shared(name); } + +pstd::Status Acl::SetUser(const std::string& userName, std::vector& op) { + auto user = GetUserLock(userName); + + std::shared_ptr tempUser = nullptr; + bool add = false; + if (!user) { // if the user not exist, create new user + user = CreatedUser(userName); + add = true; + } else { + tempUser = std::make_shared(*user); + } + + std::vector aclArgc; + ACLMergeSelectorArguments(op, &aclArgc); + + auto status = user->SetUser(aclArgc); + if (!status.ok()) { + return status; + } + + if (add) { + AddUserLock(user); + } else { + KillPubsubClientsIfNeeded(tempUser, user); + } + return pstd::Status::OK(); +} + +void Acl::KillPubsubClientsIfNeeded(const std::shared_ptr& origin, const std::shared_ptr& newUser) { + std::shared_lock l(mutex_); + bool match = true; + for (const auto& newUserSelector : newUser->selectors_) { + if (newUserSelector->HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS))) { // new user has all channels + return; + } + } + auto newChKey = newUser->AllChannelKey(); + + for (const auto& selector : origin->selectors_) { + if (selector->HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS))) { + match = false; + break; + } + if (!selector->EqualChannel(newChKey)) { + match = false; + break; + } + } + if (match) { + return; + } + g_pika_server->CheckPubsubClientKill(newUser->Name(), newChKey); +} + +uint32_t Acl::GetCommandCategoryFlagByName(const std::string& name) { + for (const auto& item : CommandCategories) { + if (item.first == name) { + return item.second; + } + } + return 0; +} + +std::string Acl::GetCommandCategoryFlagByName(const uint32_t category) { + for (const auto& item : CommandCategories) { + if (item.second == category) { + return item.first; + } + } + + return ""; +} + +std::vector Acl::GetAllCategoryName() { + std::vector result; + result.reserve(CommandCategories.size()); + for (const auto& item : CommandCategories) { + result.emplace_back(item.first); + } + return result; +} + +void Acl::ACLMergeSelectorArguments(std::vector& argv, std::vector* merged) { + bool openBracketStart = false; + std::string selector; + for (const auto& item : argv) { + if (item[0] == '(' && item[item.size() - 1] != ')') { + selector = item; + openBracketStart = true; + continue; + } + + if (openBracketStart) { + selector += " " + item; + if (item[item.size() - 1] == ')') { + openBracketStart = false; + merged->emplace_back(selector); + } + continue; + } + + merged->emplace_back(item); + } +} + +std::shared_ptr Acl::Auth(const std::string& userName, const std::string& password) { + std::shared_lock l(mutex_); + + auto user = GetUser(userName); + if (!user) { + return nullptr; + } + if (user->HasFlags(static_cast(AclUserFlag::DISABLED))) { + return nullptr; + } + + if (user->HasFlags(static_cast(AclUserFlag::NO_PASS))) { + return user; + } + + if (user->MatchPassword(pstd::sha256(password))) { + return user; + } + return nullptr; +} + +std::vector Acl::Users() { + std::shared_lock l(mutex_); + std::vector result; + result.reserve(users_.size()); + + for (const auto& item : users_) { + result.emplace_back(item.first); + } + + return result; +} + +void Acl::DescribeAllUser(std::vector* content) { + std::shared_lock l(mutex_); + content->reserve(users_.size()); + + for (const auto& item : users_) { + std::string saveContent; + saveContent += "user "; + saveContent += item.first; + + item.second->DescribeUser(&saveContent); + content->emplace_back(saveContent); + } +} + +pstd::Status Acl::SaveToFile() { + std::string aclFileName = g_pika_conf->acl_file(); + if (aclFileName.empty()) { + LOG(ERROR) << "save user to acl file, file name is empty"; + return pstd::Status::Error("acl file name is empty"); + } + + std::unique_lock wl(mutex_); + + std::unique_ptr file; + const std::string tmpFile = aclFileName + ".tmp"; + auto status = pstd::NewWritableFile(tmpFile, file); + if (!status.ok()) { + auto error = fmt::format("open acl user file:{} fail, error:{}", aclFileName, status.ToString()); + LOG(ERROR) << error; + return pstd::Status::Error(error); + } + + std::string saveContent; + for (const auto& item : users_) { + saveContent += "user "; + saveContent += item.first; + + item.second->DescribeUser(&saveContent); + saveContent += "\n"; + } + + file->Append(saveContent); + file->Sync(); + file->Close(); + + if (pstd::RenameFile(tmpFile, aclFileName) < 0) { // rename fail + return pstd::Status::Error("save acl rule to file fail. specific information see pika log"); + } + return pstd::Status::OK(); +} + +std::set Acl::DeleteUser(const std::vector& userNames) { + std::unique_lock wl(mutex_); + + std::set delUserNames; + for (const auto& userName : userNames) { + if (users_.erase(userName)) { + delUserNames.insert(userName); + } + } + + return delUserNames; +} + +std::array, 21> Acl::CommandCategories = {{ + {"keyspace", static_cast(AclCategory::KEYSPACE)}, + {"read", static_cast(AclCategory::READ)}, + {"write", static_cast(AclCategory::WRITE)}, + {"set", static_cast(AclCategory::SET)}, + {"sortedset", static_cast(AclCategory::SORTEDSET)}, + {"list", static_cast(AclCategory::LIST)}, + {"hash", static_cast(AclCategory::HASH)}, + {"string", static_cast(AclCategory::STRING)}, + {"bitmap", static_cast(AclCategory::BITMAP)}, + {"hyperloglog", static_cast(AclCategory::HYPERLOGLOG)}, + {"geo", static_cast(AclCategory::GEO)}, + {"stream", static_cast(AclCategory::STREAM)}, + {"pubsub", static_cast(AclCategory::PUBSUB)}, + {"admin", static_cast(AclCategory::ADMIN)}, + {"fast", static_cast(AclCategory::FAST)}, + {"slow", static_cast(AclCategory::SLOW)}, + {"blocking", static_cast(AclCategory::BLOCKING)}, + {"dangerous", static_cast(AclCategory::DANGEROUS)}, + {"connection", static_cast(AclCategory::CONNECTION)}, + {"transaction", static_cast(AclCategory::TRANSACTION)}, + {"scripting", static_cast(AclCategory::SCRIPTING)}, +}}; + +std::array, 3> Acl::UserFlags = {{ + {"on", static_cast(AclUserFlag::ENABLED)}, + {"off", static_cast(AclUserFlag::DISABLED)}, + {"nopass", static_cast(AclUserFlag::NO_PASS)}, +}}; + +std::array, 3> Acl::SelectorFlags = {{ + {"allkeys", static_cast(AclSelectorFlag::ALL_KEYS)}, + {"allchannels", static_cast(AclSelectorFlag::ALL_CHANNELS)}, + {"allcommands", static_cast(AclSelectorFlag::ALL_COMMANDS)}, +}}; + +const std::string Acl::DefaultUser = "default"; +const int64_t Acl::LogGroupingMaxTimeDelta = 60000; + +void Acl::AddLogEntry(int32_t reason, int32_t context, const std::string& username, const std::string& object, + const std::string& cInfo) { + int64_t nowUnix = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + { + std::unique_lock wl(mutex_); + for (const auto& item : logEntries_) { + if (item->Match(reason, context, nowUnix, object, username)) { + item->AddEntry(cInfo, nowUnix); + return; + } + } + auto entry = std::make_unique(reason, context, object, username, nowUnix, cInfo); + logEntries_.push_front(std::move(entry)); + + auto maxLen = g_pika_conf->acl_log_max_len(); + if (logEntries_.size() > maxLen) { // remove overflow log + if (maxLen == 0) { + logEntries_.clear(); + } else { + logEntries_.erase(std::next(logEntries_.begin(), maxLen), logEntries_.end()); + } + } + } +} + +void Acl::GetLog(long count, CmdRes* res) { + std::shared_lock rl(mutex_); + auto size = static_cast(logEntries_.size()); + if (count == -1) { + count = size; + } + if (count > size) { + count = size; + } + if (count == 0) { + res->AppendArrayLen(0); + return; + } + + std::vector items; + res->AppendArrayLen(static_cast(count)); + items.reserve(14); + for (const auto& item : logEntries_) { + items.clear(); + item->GetReplyInfo(&items); + res->AppendStringVector(items); + count--; + if (count == 0) { + break; + } + } +} + +void Acl::ResetLog() { + std::unique_lock wl(mutex_); + logEntries_.clear(); +} +// class Acl end + +// class ACLLogEntry +bool ACLLogEntry::Match(int32_t reason, int32_t context, int64_t ctime, const std::string& object, + const std::string& username) { + if (reason_ != reason) { + return false; + } + if (context_ != context) { + return false; + } + auto delta = ctime_ - ctime; + if (delta > Acl::LogGroupingMaxTimeDelta) { + return false; + }; + if (object_ != object) { + return false; + } + if (username_ != username) { + return false; + } + return true; +} + +void ACLLogEntry::AddEntry(const std::string& cinfo, u_int64_t ctime) { + cinfo_ = cinfo; + ctime_ = ctime; + ++count_; +} + +void ACLLogEntry::GetReplyInfo(std::vector* vector) { + vector->emplace_back("count"); + vector->emplace_back(std::to_string(count_)); + vector->emplace_back("reason"); + switch (reason_) { + case static_cast(AclDeniedCmd::CMD): + vector->emplace_back("command"); + break; + case static_cast(AclDeniedCmd::KEY): + vector->emplace_back("key"); + break; + case static_cast(AclDeniedCmd::CHANNEL): + vector->emplace_back("channel"); + break; + case static_cast(AclDeniedCmd::NO_AUTH): + vector->emplace_back("auth"); + break; + default: + vector->emplace_back("unknown"); + break; + } + + vector->emplace_back("context"); + switch (context_) { + case static_cast(AclLogCtx::TOPLEVEL): + vector->emplace_back("toplevel"); + break; + case static_cast(AclLogCtx::MULTI): + vector->emplace_back("multi"); + break; + case static_cast(AclLogCtx::LUA): + vector->emplace_back("lua"); + break; + default: + vector->emplace_back("unknown"); + break; + } + + vector->emplace_back("object"); + vector->emplace_back(object_); + vector->emplace_back("username"); + vector->emplace_back(username_); + vector->emplace_back("age-seconds"); + int64_t nowUnix = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + + char latitude[32]; + pstd::d2string(latitude, 32, static_cast(nowUnix - ctime_) / 1000); + vector->emplace_back(latitude); + vector->emplace_back("client-info"); + vector->emplace_back(cinfo_); +} + +// class ACLLogEntry end + +// class AclSelector +AclSelector::AclSelector(uint32_t flag) : flags_(flag) { + if (g_pika_conf->acl_pubsub_default()) { + AddFlags(static_cast(AclSelectorFlag::ALL_CHANNELS)); + } +} + +AclSelector::AclSelector(const AclSelector& selector) { + flags_ = selector.Flags(); + allowedCommands_ = selector.allowedCommands_; + subCommand_ = selector.subCommand_; + channels_ = selector.channels_; + commandRules_ = selector.commandRules_; + + for (const auto& item : selector.patterns_) { + auto pattern = std::make_shared(); + pattern->flags = item->flags; + pattern->pattern = item->pattern; + patterns_.emplace_back(pattern); + } +} + +pstd::Status AclSelector::SetSelector(const std::string& op) { + if (!strcasecmp(op.data(), "allkeys") || op == "~*") { + AddFlags(static_cast(AclSelectorFlag::ALL_KEYS)); + patterns_.clear(); + } else if (!strcasecmp(op.data(), "resetkeys")) { + DecFlags(static_cast(AclSelectorFlag::ALL_KEYS)); + patterns_.clear(); + } else if (!strcasecmp(op.data(), "allchannels") || !strcasecmp(op.data(), "&*")) { + AddFlags(static_cast(AclSelectorFlag::ALL_CHANNELS)); + channels_.clear(); + } else if (!strcasecmp(op.data(), "resetchannels")) { + DecFlags(static_cast(AclSelectorFlag::ALL_CHANNELS)); + channels_.clear(); + } else if (!strcasecmp(op.data(), "allcommands") || !strcasecmp(op.data(), "+@all")) { + SetAllCommandSelector(); + } else if (!strcasecmp(op.data(), "nocommands") || !strcasecmp(op.data(), "-@all")) { + RestAllCommandSelector(); + } else if (op[0] == '~' || op[0] == '%') { + if (HasFlags(static_cast(AclSelectorFlag::ALL_KEYS))) { + return pstd::Status::Error( + fmt::format("Error in ACL SETUSER modifier '{}': Adding a pattern after the * " + "pattern (or the 'allkeys' flag) is not valid and does not have any effect." + " Try 'resetkeys' to start with an empty list of patterns", + op)); + } + int flags = 0; + size_t offset = 1; + if (op[0] == '%') { + for (; offset < op.size(); offset++) { + if (toupper(op[offset]) == 'R' && !(flags & static_cast(AclPermission::READ))) { + flags |= static_cast(AclPermission::READ); + } else if (toupper(op[offset]) == 'W' && !(flags & static_cast(AclPermission::WRITE))) { + flags |= static_cast(AclPermission::WRITE); + } else if (op[offset] == '~') { + offset++; + break; + } else { + return pstd::Status::Error("Syntax error"); + } + } + } else { + flags = static_cast(AclPermission::ALL); + } + + if (pstd::isspace(op)) { + return pstd::Status::Error("Syntax error"); + } + + InsertKeyPattern(op.substr(offset, std::string::npos), flags); + DecFlags(static_cast(AclSelectorFlag::ALL_KEYS)); + } else if (op[0] == '&') { + if (HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS))) { + return pstd::Status::Error( + "Adding a pattern after the * pattern (or the 'allchannels' flag) is not valid and does not have any effect. " + "Try 'resetchannels' to start with an empty list of channels"); + } + if (pstd::isspace(op)) { + return pstd::Status::Error("Syntax error"); + } + InsertChannel(op.substr(1, std::string::npos)); + DecFlags(static_cast(AclSelectorFlag::ALL_CHANNELS)); + } else if (op[0] == '+' && op[1] != '@') { + auto status = SetCommandOp(op, true); + if (!status.ok()) { + return status; + } + UpdateCommonRule(op.data() + 1, true); + } else if (op[0] == '-' && op[1] != '@') { + auto status = SetCommandOp(op, false); + if (!status.ok()) { + return status; + } + UpdateCommonRule(op.data() + 1, false); + } else if ((op[0] == '+' || op[0] == '-') && op[1] == '@') { + bool allow = op[0] == '+' ? true : false; + if (!SetSelectorCommandBitsForCategory(op.data() + 1, allow)) { + return pstd::Status::Error("Unknown command or category name in ACL"); + } + } else { + return pstd::Status::Error("Syntax error"); + } + return pstd::Status(); +} + +pstd::Status AclSelector::SetSelectorFromOpSet(const std::string& opSet) { + if (opSet[0] != '(' || opSet[opSet.size() - 1] != ')') { + return pstd::Status::Error("Unmatched parenthesis in acl selector starting at" + opSet); + } + + std::vector args; + pstd::StringSplit(opSet.substr(1, opSet.size() - 2), ' ', args); + + for (const auto& item : args) { + auto status = SetSelector(item); + if (!status.ok()) { + return status; + } + } + return pstd::Status().OK(); +} + +bool AclSelector::SetSelectorCommandBitsForCategory(const std::string& categoryName, bool allow) { + std::string lowerCategoryName(categoryName); + std::transform(categoryName.begin(), categoryName.end(), lowerCategoryName.begin(), ::tolower); + auto category = Acl::GetCommandCategoryFlagByName(lowerCategoryName.data() + 1); + if (!category) { // not find category + return false; + } + UpdateCommonRule(categoryName, allow); + for (const auto& cmd : *g_pika_cmd_table_manager->cmds_) { + if (cmd.second->AclCategory() & category) { // this cmd belongs to this category + ChangeSelector(cmd.second.get(), allow); + } + } + return true; +} + +void AclSelector::SetAllCommandSelector() { + AddFlags(static_cast(AclSelectorFlag::ALL_COMMANDS)); + allowedCommands_.set(); + for (const auto& cmd : *g_pika_cmd_table_manager->cmds_) { + if (cmd.second->HasSubCommand()) { + SetSubCommand(cmd.second->GetCmdId()); + } + } + CleanCommandRule(); +} + +void AclSelector::RestAllCommandSelector() { + DecFlags(static_cast(AclSelectorFlag::ALL_COMMANDS)); + allowedCommands_.reset(); + ResetSubCommand(); + CleanCommandRule(); +} + +void AclSelector::InsertKeyPattern(const std::string& str, uint32_t flags) { + for (const auto& item : patterns_) { + if (item->pattern == str) { + item->flags |= flags; + return; + } + } + auto pattern = std::make_shared(); + pattern->flags = flags; + pattern->pattern = str; + patterns_.emplace_back(pattern); + return; +} + +void AclSelector::InsertChannel(const std::string& str) { + for (const auto& item : channels_) { + if (item == str) { + return; + } + } + channels_.emplace_back(str); +} + +void AclSelector::ChangeSelector(const Cmd* cmd, bool allow) { + if (allow) { + allowedCommands_.set(cmd->GetCmdId()); + if (cmd->HasSubCommand()) { + SetSubCommand(cmd->GetCmdId()); + } + } else { + allowedCommands_.reset(cmd->GetCmdId()); + DecFlags(static_cast(AclSelectorFlag::ALL_COMMANDS)); + if (cmd->HasSubCommand()) { + ResetSubCommand(cmd->GetCmdId()); + } + } +} + +void AclSelector::ChangeSelector(const std::shared_ptr& cmd, bool allow) { ChangeSelector(cmd.get(), allow); } + +pstd::Status AclSelector::ChangeSelector(const std::shared_ptr& cmd, const std::string& subCmd, bool allow) { + if (cmd->HasSubCommand()) { + auto index = cmd->SubCmdIndex(subCmd); + if (index == -1) { + return pstd::Status::Error("Unknown command or category name in ACL"); + } + if (allow) { + SetSubCommand(cmd->GetCmdId(), index); + } else { + ResetSubCommand(cmd->GetCmdId(), index); + } + } + return pstd::Status::OK(); +} + +void AclSelector::SetSubCommand(uint32_t cmdId) { subCommand_[cmdId] = 0xFFFFFFFF; } + +void AclSelector::SetSubCommand(uint32_t cmdId, uint32_t subCmdIndex) { subCommand_[cmdId] |= (1 << subCmdIndex); } + +void AclSelector::ResetSubCommand() { subCommand_.clear(); } + +void AclSelector::ResetSubCommand(uint32_t cmdId) { subCommand_[cmdId] = 0; } + +void AclSelector::ResetSubCommand(uint32_t cmdId, uint32_t subCmdIndex) { + DecFlags(static_cast(AclSelectorFlag::ALL_COMMANDS)); + subCommand_[cmdId] &= ~(1 << subCmdIndex); +} + +bool AclSelector::CheckSubCommand(uint32_t cmdId, uint32_t subCmdIndex) { + if (subCmdIndex < 0) { + return false; + } + auto bit = subCommand_.find(cmdId); + if (bit == subCommand_.end()) { + return false; + } + + return bit->second & (1 << subCmdIndex); +} + +void AclSelector::ACLDescribeSelector(std::string* str) { + if (HasFlags(static_cast(AclSelectorFlag::ALL_KEYS))) { + str->append(" ~*"); + } else { + for (const auto& item : patterns_) { + str->append(" "); + item->ToString(str); + } + } + + // Pub/sub channel patterns + if (HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS))) { + str->append(" &*"); + } else if (channels_.empty()) { + str->append(" resetchannels"); + } else { + for (const auto& item : channels_) { + str->append(" &" + item); + } + } + + // Command rules + DescribeSelectorCommandRules(str); +} + +void AclSelector::ACLDescribeSelector(std::vector& vector) { + vector.emplace_back("commands"); + if (allowedCommands_.test(USER_COMMAND_BITS_COUNT - 1)) { + if (commandRules_.empty()) { + vector.emplace_back("+@all"); + } else { + vector.emplace_back("+@all " + commandRules_); + } + } else { + if (commandRules_.empty()) { + vector.emplace_back("-@all"); + } else { + vector.emplace_back("-@all " + commandRules_); + } + } + + vector.emplace_back("key"); + if (HasFlags(static_cast(AclSelectorFlag::ALL_KEYS))) { + vector.emplace_back("~*"); + } else if (patterns_.empty()) { + vector.emplace_back(""); + } else { + std::string keys; + for (auto it = patterns_.begin(); it != patterns_.end(); ++it) { + if (it != patterns_.begin()) { + keys += " "; + (*it)->ToString(&keys); + } + } + vector.emplace_back(keys); + } + + vector.emplace_back("channels"); + if (HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS))) { + vector.emplace_back("&*"); + } else if (channels_.empty()) { + vector.emplace_back(""); + } else if (channels_.size() == 1) { + vector.emplace_back("&" + channels_.front()); + } else { + vector.emplace_back(fmt::format("{}", fmt::join(channels_, " &"))); + } +} + +AclDeniedCmd AclSelector::CheckCanExecCmd(std::shared_ptr& cmd, int8_t subCmdIndex, + const std::vector& keys, std::string* errKey) { + if (!HasFlags(static_cast(AclSelectorFlag::ALL_COMMANDS)) && !(cmd->flag() & kCmdFlagsNoAuth)) { + if (subCmdIndex < 0) { + if (!allowedCommands_.test(cmd->GetCmdId())) { + return AclDeniedCmd::CMD; + } + } else { // if the command has subCmd + if (!CheckSubCommand(cmd->GetCmdId(), subCmdIndex)) { + return AclDeniedCmd::CMD; + } + } + } + + // key match + if (!HasFlags(static_cast(AclSelectorFlag::ALL_KEYS)) && !keys.empty() && !cmd->hasFlag(kCmdFlagsPubSub)) { + for (const auto& key : keys) { + // if the key is empty, skip, because some command keys for write categories are empty + if (!key.empty() && !CheckKey(key, cmd->flag())) { + if (errKey) { + *errKey = key; + } + return AclDeniedCmd::KEY; + } + } + } + + // channel match + if (!HasFlags(static_cast(AclSelectorFlag::ALL_CHANNELS)) && cmd->hasFlag(kCmdFlagsPubSub)) { + bool isPattern = cmd->name() == kCmdNamePSubscribe || cmd->name() == kCmdNamePUnSubscribe; + for (const auto& key : keys) { + if (!CheckChannel(key, isPattern)) { + if (errKey) { + *errKey = key; + } + return AclDeniedCmd::CHANNEL; + } + } + } + return AclDeniedCmd::OK; +} + +bool AclSelector::EqualChannel(const std::vector& allChannel) { + for (const auto& item : channels_) { + if (std::count(allChannel.begin(), allChannel.end(), item) == 0) { + return false; + } + } + return true; +} + +void AclSelector::DescribeSelectorCommandRules(std::string* str) { + allowedCommands_.test(USER_COMMAND_BITS_COUNT - 1) ? str->append(" +@all") : str->append(" -@all"); + + // Category + if (!commandRules_.empty()) { + str->append(" "); + str->append(commandRules_); + } +} + +pstd::Status AclSelector::SetCommandOp(const std::string& op, bool allow) { + std::string _op(op.data() + 1); + pstd::StringToLower(_op); + if (_op.find('|') == std::string::npos) { + auto cmd = g_pika_cmd_table_manager->GetCmd(_op); + if (!cmd) { + return pstd::Status::Error("Unknown command or category name in ACL"); + } + ChangeSelector(cmd, allow); + return pstd::Status::OK(); + } else { + /* Split the command and subcommand parts. */ + std::vector cmds; + pstd::StringSplit(_op, '|', cmds); + + /* The subcommand cannot be empty, so things like CONFIG| + * are syntax errors of course. */ + if (cmds.size() != 2) { + return pstd::Status::Error("Allowing first-arg of a subcommand is not supported"); + } + + auto parentCmd = g_pika_cmd_table_manager->GetCmd(cmds[0]); + if (!parentCmd) { + return pstd::Status::Error("Unknown command or category name in ACL"); + } + + return ChangeSelector(parentCmd, cmds[1], allow); + + // not support Redis ACL `first-arg` feature + } +} + +void AclSelector::UpdateCommonRule(const std::string& rule, bool allow) { + std::string _rule(rule); + pstd::StringToLower(_rule); + RemoveCommonRule(_rule); + if (commandRules_.empty()) { + commandRules_ += allow ? "+" : "-"; + } else { + commandRules_ += allow ? " +" : " -"; + } + commandRules_ += _rule; +} + +void AclSelector::RemoveCommonRule(const std::string& rule) { + if (commandRules_.empty()) { + return; + } + + const size_t ruleLen = rule.size(); + + size_t start = 0; + while (true) { + start = commandRules_.find(rule, start); + if (start == std::string::npos) { + return; + } + + size_t delNum = 0; // the length to be deleted this time + if (start + ruleLen >= commandRules_.size()) { // the remaining commandRule == rule, delete to end + delNum = ruleLen; + --start; + ++delNum; + } else { + if (commandRules_[start + ruleLen] == ' ') { + delNum = ruleLen + 1; + } else if (commandRules_[start + ruleLen] == '|') { + size_t end = commandRules_.find(' ', start); // find next ' ' + if (end == std::string::npos) { // not find ' ', delete to end + delNum = commandRules_.size() - start; + --start; + ++delNum; + } else { + delNum = end + 1 - start; + } + } else { + start += ruleLen; + continue; // not match + } + } + + if (start > 0) { // the rule not included '-'/'+', but need delete need + --start; + ++delNum; // star position moved one forward So delNum takes +1 + } + + commandRules_.erase(start, delNum); + } +} + +void AclSelector::CleanCommandRule() { commandRules_.clear(); } + +bool AclSelector::CheckKey(const std::string& key, const uint32_t cmdFlag) { + uint32_t selectorFlag = 0; + if (cmdFlag & kCmdFlagsRead) { + selectorFlag |= static_cast(AclPermission::READ); + } + if (cmdFlag & kCmdFlagsWrite) { + selectorFlag |= static_cast(AclPermission::WRITE); + } + if ((selectorFlag & static_cast(AclPermission::WRITE)) && + (selectorFlag & static_cast(AclPermission::READ))) { + selectorFlag |= static_cast(AclPermission::ALL); + } + + for (const auto& item : patterns_) { + if ((item->flags & selectorFlag) != selectorFlag) { + continue; + } + + if (pstd::stringmatchlen(item->pattern.data(), static_cast(item->pattern.size()), key.data(), + static_cast(key.size()), 0)) { + return true; + } + } + return false; +} + +bool AclSelector::CheckChannel(const std::string& key, bool isPattern) { + for (const auto& channel : channels_) { + if (isPattern ? (channel == key) + : (pstd::stringmatchlen(channel.data(), static_cast(channel.size()), key.data(), + static_cast(key.size()), 0))) { + return true; + } + } + return false; +} +// class AclSelector end \ No newline at end of file diff --git a/src/net/include/net_pubsub.h b/src/net/include/net_pubsub.h index 28ef98a764..86541f771c 100644 --- a/src/net/include/net_pubsub.h +++ b/src/net/include/net_pubsub.h @@ -56,6 +56,9 @@ class PubSubThread : public Thread { // Move into pubsub thread void MoveConnIn(const std::shared_ptr& conn, const NotifyType& notify_type); + void ConnCanSubscribe(const std::vector& allChannel, + const std::function&)>& func); + enum ReadyState { kNotReady, kReady, @@ -72,9 +75,12 @@ class PubSubThread : public Thread { void UpdateConnReadyState(int fd, const ReadyState& state); bool IsReady(int fd); + int ClientPubSubChannelSize(const std::shared_ptr& conn); + int ClientPubSubChannelPatternSize(const std::shared_ptr& conn); private: void RemoveConn(const std::shared_ptr& conn); + void CloseConn(const std::shared_ptr& conn); int ClientChannelSize(const std::shared_ptr& conn); diff --git a/src/net/src/dispatch_thread.cc b/src/net/src/dispatch_thread.cc index fd70e15401..d98c44b68b 100644 --- a/src/net/src/dispatch_thread.cc +++ b/src/net/src/dispatch_thread.cc @@ -8,8 +8,6 @@ #include #include "net/src/dispatch_thread.h" -#include "net/src/net_item.h" -#include "net/src/net_multiplexer.h" #include "net/src/worker_thread.h" namespace net { @@ -19,7 +17,7 @@ DispatchThread::DispatchThread(int port, int work_num, ConnFactory* conn_factory : ServerThread::ServerThread(port, cron_interval, handle), last_thread_(0), work_num_(work_num), - queue_limit_(queue_limit){ + queue_limit_(queue_limit) { for (int i = 0; i < work_num_; i++) { worker_thread_.emplace_back(std::make_unique(conn_factory, this, queue_limit, cron_interval)); } @@ -30,7 +28,7 @@ DispatchThread::DispatchThread(const std::string& ip, int port, int work_num, Co : ServerThread::ServerThread(ip, port, cron_interval, handle), last_thread_(0), work_num_(work_num), - queue_limit_(queue_limit){ + queue_limit_(queue_limit) { for (int i = 0; i < work_num_; i++) { worker_thread_.emplace_back(std::make_unique(conn_factory, this, queue_limit, cron_interval)); } @@ -66,9 +64,8 @@ int DispatchThread::StartThread() { } // Adding timer tasks and run timertaskThread - timerTaskThread_.AddTimerTask( - "blrpop_blocking_info_scan", 250, true, [this] { this->ScanExpiredBlockedConnsOfBlrpop();}); - + timerTaskThread_.AddTimerTask("blrpop_blocking_info_scan", 250, true, + [this] { this->ScanExpiredBlockedConnsOfBlrpop(); }); timerTaskThread_.StartThread(); return ServerThread::StartThread(); @@ -261,12 +258,23 @@ void DispatchThread::ScanExpiredBlockedConnsOfBlrpop() { void DispatchThread::SetQueueLimit(int queue_limit) { queue_limit_ = queue_limit; } +void DispatchThread::AllConn(const std::function&)>& func) { + std::unique_lock l(block_mtx_); + for (const auto& item : worker_thread_) { + std::unique_lock wl(item->rwlock_); + for (const auto& conn : item->conns_) { + func(conn.second); + } + } +} + /** * @param keys format: tablename + key,because can watch the key of different db */ -void DispatchThread::AddWatchKeys(const std::unordered_set &keys, const std::shared_ptr& client_conn) { +void DispatchThread::AddWatchKeys(const std::unordered_set& keys, + const std::shared_ptr& client_conn) { std::lock_guard lg(watch_keys_mu_); - for (const auto &key : keys) { + for (const auto& key : keys) { if (key_conns_map_.count(key) == 0) { key_conns_map_.emplace(); } @@ -277,8 +285,8 @@ void DispatchThread::AddWatchKeys(const std::unordered_set &keys, c void DispatchThread::RemoveWatchKeys(const std::shared_ptr& client_conn) { std::lock_guard lg(watch_keys_mu_); - auto &keys = conn_keys_map_[client_conn]; - for (const auto &key : keys) { + auto& keys = conn_keys_map_[client_conn]; + for (const auto& key : keys) { if (key_conns_map_.count(key) == 0 || key_conns_map_[key].count(client_conn) == 0) { continue; } @@ -290,14 +298,14 @@ void DispatchThread::RemoveWatchKeys(const std::shared_ptr& client_conn conn_keys_map_.erase(client_conn); } -std::vector> DispatchThread::GetInvolvedTxn(const std::vector &keys) { +std::vector> DispatchThread::GetInvolvedTxn(const std::vector& keys) { std::lock_guard lg(watch_keys_mu_); auto involved_conns = std::vector>{}; - for (const auto &key : keys) { + for (const auto& key : keys) { if (key_conns_map_.count(key) == 0 || key_conns_map_[key].empty()) { continue; } - for(auto &client_conn : key_conns_map_[key]) { + for (auto& client_conn : key_conns_map_[key]) { involved_conns.emplace_back(client_conn); } } @@ -307,7 +315,7 @@ std::vector> DispatchThread::GetInvolvedTxn(const std:: std::vector> DispatchThread::GetAllTxns() { std::lock_guard lg(watch_keys_mu_); auto involved_conns = std::vector>{}; - for(auto &[client_conn, _] : conn_keys_map_) { + for (auto& [client_conn, _] : conn_keys_map_) { involved_conns.emplace_back(client_conn); } return involved_conns; @@ -316,7 +324,7 @@ std::vector> DispatchThread::GetAllTxns() { std::vector> DispatchThread::GetDBTxns(std::string db_name) { std::lock_guard lg(watch_keys_mu_); auto involved_conns = std::vector>{}; - for(auto &[db_key, client_conns] : key_conns_map_) { + for (auto& [db_key, client_conns] : key_conns_map_) { if (db_key.find(db_name) == 0) { involved_conns.insert(involved_conns.end(), client_conns.begin(), client_conns.end()); } diff --git a/src/net/src/dispatch_thread.h b/src/net/src/dispatch_thread.h index 09afc478bf..0fb1b5c89c 100644 --- a/src/net/src/dispatch_thread.h +++ b/src/net/src/dispatch_thread.h @@ -89,6 +89,8 @@ class DispatchThread : public ServerThread { void SetQueueLimit(int queue_limit) override; + void AllConn(const std::function&)>& func); + /** * BlPop/BrPop used start */ diff --git a/src/net/src/net_pubsub.cc b/src/net/src/net_pubsub.cc index b9b3b006c0..ca9bc2f788 100644 --- a/src/net/src/net_pubsub.cc +++ b/src/net/src/net_pubsub.cc @@ -47,7 +47,7 @@ void PubSubThread::ConnHandle::UpdateReadyState(const ReadyState& state) { ready bool PubSubThread::ConnHandle::IsReady() { return ready_state == PubSubThread::ReadyState::kReady; } -PubSubThread::PubSubThread() { +PubSubThread::PubSubThread() { set_thread_name("PubSubThread"); net_multiplexer_.reset(CreateNetMultiplexer()); net_multiplexer_->Initialize(); @@ -100,10 +100,34 @@ bool PubSubThread::IsReady(int fd) { return false; } +int PubSubThread::ClientPubSubChannelSize(const std::shared_ptr& conn) { + int subscribed = 0; + std::lock_guard l(channel_mutex_); + for (auto& channel : pubsub_channel_) { + auto conn_ptr = std::find(channel.second.begin(), channel.second.end(), conn); + if (conn_ptr != channel.second.end()) { + subscribed++; + } + } + return subscribed; +} + +int PubSubThread::ClientPubSubChannelPatternSize(const std::shared_ptr& conn) { + int subscribed = 0; + std::lock_guard l(pattern_mutex_); + for (auto& channel : pubsub_pattern_) { + auto conn_ptr = std::find(channel.second.begin(), channel.second.end(), conn); + if (conn_ptr != channel.second.end()) { + subscribed++; + } + } + return subscribed; +} + void PubSubThread::RemoveConn(const std::shared_ptr& conn) { { std::lock_guard lock(pattern_mutex_); - for (auto & it : pubsub_pattern_) { + for (auto& it : pubsub_pattern_) { for (auto conn_ptr = it.second.begin(); conn_ptr != it.second.end(); conn_ptr++) { if ((*conn_ptr) == conn) { conn_ptr = it.second.erase(conn_ptr); @@ -115,7 +139,7 @@ void PubSubThread::RemoveConn(const std::shared_ptr& conn) { { std::lock_guard lock(channel_mutex_); - for (auto & it : pubsub_channel_) { + for (auto& it : pubsub_channel_) { for (auto conn_ptr = it.second.begin(); conn_ptr != it.second.end(); conn_ptr++) { if ((*conn_ptr) == conn) { conn_ptr = it.second.erase(conn_ptr); @@ -126,6 +150,15 @@ void PubSubThread::RemoveConn(const std::shared_ptr& conn) { } } +void PubSubThread::CloseConn(const std::shared_ptr& conn) { + CloseFd(conn); + net_multiplexer_->NetDelEvent(conn->fd(), 0); + { + std::lock_guard l(rwlock_); + conns_.erase(conn->fd()); + } +} + int PubSubThread::Publish(const std::string& channel, const std::string& msg) { // TODO(LIBA-S): change the Publish Mode to Asynchronous std::lock_guard lk(pub_mutex_); @@ -178,7 +211,7 @@ void PubSubThread::Subscribe(const std::shared_ptr& conn, const std::ve MoveConnIn(conn, net::NotifyType::kNotiWait); } - for (const auto & channel : channels) { + for (const auto& channel : channels) { if (pattern) { // if pattern mode, register channel to map std::lock_guard channel_lock(pattern_mutex_); if (pubsub_pattern_.find(channel) != pubsub_pattern_.end()) { @@ -192,7 +225,7 @@ void PubSubThread::Subscribe(const std::shared_ptr& conn, const std::ve pubsub_pattern_[channel] = conns; ++subscribed; } - result->push_back(std::make_pair(channel, subscribed)); + result->emplace_back(channel, subscribed); } else { // if general mode, reigster channel to map std::lock_guard channel_lock(channel_mutex_); if (pubsub_channel_.find(channel) != pubsub_channel_.end()) { @@ -206,7 +239,7 @@ void PubSubThread::Subscribe(const std::shared_ptr& conn, const std::ve pubsub_channel_[channel] = conns; ++subscribed; } - result->push_back(std::make_pair(channel, subscribed)); + result->emplace_back(channel, subscribed); } } } @@ -223,12 +256,12 @@ int PubSubThread::UnSubscribe(const std::shared_ptr& conn, const std::v exist = false; } if (channels.empty()) { // if client want to unsubscribe all of channels - if (pattern) { // all of pattern channels + if (pattern) { // all of pattern channels std::lock_guard l(pattern_mutex_); for (auto& channel : pubsub_pattern_) { auto conn_ptr = std::find(channel.second.begin(), channel.second.end(), conn); if (conn_ptr != channel.second.end()) { - result->push_back(std::make_pair(channel.first, --subscribed)); + result->emplace_back(channel.first, --subscribed); } } } else { @@ -236,7 +269,7 @@ int PubSubThread::UnSubscribe(const std::shared_ptr& conn, const std::v for (auto& channel : pubsub_channel_) { auto conn_ptr = std::find(channel.second.begin(), channel.second.end(), conn); if (conn_ptr != channel.second.end()) { - result->push_back(std::make_pair(channel.first, --subscribed)); + result->emplace_back(channel.first, --subscribed); } } } @@ -246,7 +279,7 @@ int PubSubThread::UnSubscribe(const std::shared_ptr& conn, const std::v return 0; } - for (const auto & channel : channels) { + for (const auto& channel : channels) { if (pattern) { // if pattern mode, unsubscribe the channels of specified std::lock_guard l(pattern_mutex_); auto channel_ptr = pubsub_pattern_.find(channel); @@ -255,12 +288,12 @@ int PubSubThread::UnSubscribe(const std::shared_ptr& conn, const std::v if (it != channel_ptr->second.end()) { channel_ptr->second.erase(std::remove(channel_ptr->second.begin(), channel_ptr->second.end(), conn), channel_ptr->second.end()); - result->push_back(std::make_pair(channel, --subscribed)); + result->emplace_back(channel, --subscribed); } else { - result->push_back(std::make_pair(channel, subscribed)); + result->emplace_back(channel, subscribed); } } else { - result->push_back(std::make_pair(channel, 0)); + result->emplace_back(channel, 0); } } else { // if general mode, unsubscribe the channels of specified std::lock_guard l(channel_mutex_); @@ -270,12 +303,12 @@ int PubSubThread::UnSubscribe(const std::shared_ptr& conn, const std::v if (it != channel_ptr->second.end()) { channel_ptr->second.erase(std::remove(channel_ptr->second.begin(), channel_ptr->second.end(), conn), channel_ptr->second.end()); - result->push_back(std::make_pair(channel, --subscribed)); + result->emplace_back(channel, --subscribed); } else { - result->push_back(std::make_pair(channel, subscribed)); + result->emplace_back(channel, subscribed); } } else { - result->push_back(std::make_pair(channel, 0)); + result->emplace_back(channel, 0); } } } @@ -299,8 +332,8 @@ void PubSubThread::PubSubChannels(const std::string& pattern, std::vector(channel.first.size()), - pattern.c_str(), static_cast(pattern.size()), 0)) { + if (pstd::stringmatchlen(channel.first.c_str(), static_cast(channel.first.size()), pattern.c_str(), + static_cast(pattern.size()), 0)) { if (!channel.second.empty()) { result->push_back(channel.first); } @@ -313,14 +346,14 @@ void PubSubThread::PubSubNumSub(const std::vector& channels, std::vector>* result) { int subscribed; std::lock_guard l(channel_mutex_); - for (const auto & i : channels) { + for (const auto& i : channels) { subscribed = 0; for (auto& channel : pubsub_channel_) { if (channel.first == i) { subscribed = static_cast(channel.second.size()); } } - result->push_back(std::make_pair(i, subscribed)); + result->emplace_back(i, subscribed); } } @@ -333,6 +366,46 @@ int PubSubThread::PubSubNumPat() { return subscribed; } +void PubSubThread::ConnCanSubscribe(const std::vector& allChannel, + const std::function&)>& func) { + { + std::lock_guard l(channel_mutex_); + for (auto& item : pubsub_channel_) { + for (auto it = item.second.rbegin(); it != item.second.rend(); it++) { + if (func(*it) && (allChannel.empty() || !std::count(allChannel.begin(), allChannel.end(), item.first))) { + item.second.erase(std::next(it).base()); + CloseConn(*it); + } + } // for end + } + } + + { + std::lock_guard l(pattern_mutex_); + for (auto& item : pubsub_pattern_) { + for (auto it = item.second.rbegin(); it != item.second.rend(); it++) { + bool kill = false; + if (func(*it)) { + if (allChannel.empty()) { + kill = true; + } + for (const auto& channelName : allChannel) { + if (kill || !pstd::stringmatchlen(channelName.c_str(), static_cast(channelName.size()), + item.first.c_str(), static_cast(item.first.size()), 0)) { + kill = true; + break; + } + } + } + if (kill) { + item.second.erase(std::next(it).base()); + CloseConn(*it); + } + } + } + } +} + void* PubSubThread::ThreadMain() { int nfds; NetFiredEvent* pfe; @@ -407,8 +480,8 @@ void* PubSubThread::ThreadMain() { // Send message to a channel pattern's clients pattern_mutex_.lock(); for (auto& it : pubsub_pattern_) { - if (pstd::stringmatchlen(it.first.c_str(), static_cast(it.first.size()), - channel.c_str(), static_cast(channel.size()), 0)) { + if (pstd::stringmatchlen(it.first.c_str(), static_cast(it.first.size()), channel.c_str(), + static_cast(channel.size()), 0)) { for (size_t i = 0; i < it.second.size(); i++) { if (!IsReady(it.second[i]->fd())) { continue; @@ -509,5 +582,4 @@ void PubSubThread::Cleanup() { } conns_.clear(); } - }; // namespace net diff --git a/src/pika.cc b/src/pika.cc index 8128fa00e7..520e2c8f6f 100644 --- a/src/pika.cc +++ b/src/pika.cc @@ -85,7 +85,7 @@ static void daemonize() { if (fork()) { exit(0); /* parent exits */ } - setsid(); /* create a new session */ + setsid(); /* create a new session */ } static void close_std() { @@ -207,11 +207,17 @@ int main(int argc, char* argv[]) { LOG(INFO) << "Server at: " << path; g_pika_cmd_table_manager = std::make_unique(); + g_pika_cmd_table_manager->InitCmdTable(); g_pika_server = new PikaServer(); g_pika_rm = std::make_unique(); g_network_statistic = std::make_unique(); g_pika_server->InitDBStruct(); + auto status = g_pika_server->InitAcl(); + if (!status.ok()) { + LOG(FATAL) << status.ToString(); + } + if (g_pika_conf->daemonize()) { close_std(); } diff --git a/src/pika_acl.cc b/src/pika_acl.cc new file mode 100644 index 0000000000..0a3a1f3e14 --- /dev/null +++ b/src/pika_acl.cc @@ -0,0 +1,323 @@ +// Copyright (c) 2015-present, Qihoo, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. + +#include + +#include "include/pika_acl.h" +#include "include/pika_client_conn.h" +#include "include/pika_cmd_table_manager.h" + +const static int AclGenPassMaxBit = 4096; + +extern std::unique_ptr g_pika_cmd_table_manager; + +void PikaAclCmd::Do(std::shared_ptr slot) { + if (subCmd_ == "cat") { + Cat(); + } else if (subCmd_ == "deluser") { + DelUser(); + } else if (subCmd_ == "dryrun") { + DryRun(); + } else if (subCmd_ == "genpass") { + GenPass(); + } else if (subCmd_ == "getuser") { + GetUser(); + } else if (subCmd_ == "list") { + List(); + } else if (subCmd_ == "load") { + Load(); + } else if (subCmd_ == "log") { + Log(); + } else if (subCmd_ == "save") { + Save(); + } else if (subCmd_ == "setuser") { + SetUser(); + } else if (subCmd_ == "users") { + Users(); + } else if (subCmd_ == "whoami") { + WhoAmI(); + } else if (subCmd_ == "help") { + Help(); + } else { + res_.SetRes(CmdRes::kSyntaxErr, KCmdNameAcl); + return; + } +} + +void PikaAclCmd::DoInitial() { + if (!CheckArg(argv_.size())) { + res_.SetRes(CmdRes::kWrongNum, KCmdNameAcl); + return; + } + + subCmd_ = argv_[1]; + pstd::StringToLower(subCmd_); + + if (argv_.size() < 3) { + if (subCmd_ == "setuser" || subCmd_ == "deluser" || subCmd_ == "getuser") { + res_.SetRes(CmdRes::kWrongNum, fmt::format("'acl|{}'", subCmd_)); + return; + } + } + + if (subCmd_ == "dryrun" && argv_.size() < 4) { + res_.SetRes(CmdRes::kWrongNum, "'acl|dryrun'"); + return; + } + if (subCmd_ == "log" && argv_.size() != 2 && argv_.size() != 3) { + res_.SetRes(CmdRes::kWrongNum, "'acl|log'"); + return; + } + + if (subCmd_ == "save" || subCmd_ == "load") { + if (g_pika_conf->acl_file().empty()) { + res().SetRes(CmdRes::kErrOther, + "This Pika is not configured to use an ACL file. You may want to specify users via the " + "ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file " + "set) in order to store users in the Pika configuration."); + return; + } + } +} + +void PikaAclCmd::Cat() { + if (argv_.size() > 3) { + res().SetRes(CmdRes::kErrOther, "unknown subcommand or wrong number of arguments for 'CAT'"); + return; + } + if (argv_.size() == 2) { + res().AppendStringVector(Acl::GetAllCategoryName()); + return; + } + auto category = Acl::GetCommandCategoryFlagByName(argv_[2]); + if (category == 0) { + res().SetRes(CmdRes::kErrOther, fmt::format("Unknown category '{}'", argv_[2])); + return; + } + res().AppendStringVector(g_pika_cmd_table_manager->GetAclCategoryCmdNames(category)); +} + +void PikaAclCmd::DelUser() { + for (auto it = argv_.begin() + 2; it != argv_.end(); ++it) { + if (it->data() == Acl::DefaultUser) { + res().SetRes(CmdRes::kErrOther, "The 'default' user cannot be removed"); + return; + } + } + + std::vector userNames(argv_.begin() + 2, argv_.end()); + auto delUserNames = g_pika_server->Acl()->DeleteUser(userNames); + res().AppendInteger(static_cast(delUserNames.size())); + + g_pika_server->AllClientUnAuth(delUserNames); +} + +void PikaAclCmd::DryRun() { + auto user = g_pika_server->Acl()->GetUserLock(argv_[2]); + + if (!user) { + res().SetRes(CmdRes::kErrOther, fmt::format("User '{}' not found", argv_[2])); + return; + } + auto cmd = g_pika_cmd_table_manager->GetCmd(argv_[3]); + + if (!cmd) { + res().SetRes(CmdRes::kErrOther, fmt::format("Command '{}' not found", argv_[3])); + return; + } + + PikaCmdArgsType args; + if (argv_.size() > 4) { + args = PikaCmdArgsType(argv_.begin() + 3, argv_.end()); + } + if (!cmd->CheckArg(args.size())) { + res().SetRes(CmdRes::kWrongNum, cmd->name()); + return; + } + + int8_t subCmdIndex = -1; + AclDeniedCmd checkRes = user->CheckUserPermission(cmd, args, subCmdIndex, nullptr); + + switch (checkRes) { + case AclDeniedCmd::OK: + res().SetRes(CmdRes::kOk); + break; + case AclDeniedCmd::CMD: + res().SetRes(CmdRes::kErrOther, + cmd->HasSubCommand() + ? fmt::format("This user has no permissions to run the '{}|{}' command", argv_[3], argv_[4]) + : fmt::format("This user has no permissions to run the '{}' command", argv_[3])); + break; + case AclDeniedCmd::KEY: + res().SetRes(CmdRes::kErrOther, + cmd->HasSubCommand() + ? fmt::format("This user has no permissions to run the '{}|{}' key", argv_[3], argv_[4]) + : fmt::format("This user has no permissions to run the '{}' key", argv_[3])); + break; + case AclDeniedCmd::CHANNEL: + res().SetRes(CmdRes::kErrOther, + cmd->HasSubCommand() + ? fmt::format("This user has no permissions to run the '{}|{}' channel", argv_[3], argv_[4]) + : fmt::format("This user has no permissions to run the '{}' channel", argv_[3])); + break; + case AclDeniedCmd::NUMBER: + res().SetRes(CmdRes::kErrOther, fmt::format("wrong number of arguments for '{}' command", argv_[3])); + break; + default: + break; + } +} + +void PikaAclCmd::GenPass() { + int bits = 256; + if (argv_.size() > 2) { + try { + bits = std::stoi(argv_[2]); + } catch (std::exception& e) { + res().SetRes(CmdRes::kErrOther, fmt::format("Invalid bits value: {}", argv_[2])); + return; + } + } + + if (bits <= 0 || bits > AclGenPassMaxBit) { + res().SetRes( + CmdRes::kErrOther, + fmt::format( + "ACL GENPASS argument must be the number of bits for the output password, a positive number up to 4096 {}", + bits)); + return; + } + + std::string pass = pstd::getRandomHexChars((bits + 3) / 4); + res().AppendString(pass); +} + +void PikaAclCmd::GetUser() { + auto user = g_pika_server->Acl()->GetUserLock(argv_[2]); + + if (!user) { + res().AppendStringLen(-1); + return; + } + + user->GetUserDescribe(&res_); +} + +void PikaAclCmd::List() { + std::vector result; + g_pika_server->Acl()->DescribeAllUser(&result); + + res().AppendStringVector(result); +} + +void PikaAclCmd::Load() { + std::set toUnAuthUsers; + auto status = g_pika_server->Acl()->LoadUserFromFile(&toUnAuthUsers); + if (status.ok()) { + res().SetRes(CmdRes::kOk); + g_pika_server->AllClientUnAuth(toUnAuthUsers); + return; + } + + res().SetRes(CmdRes::kErrOther, status.ToString()); +} + +void PikaAclCmd::Log() { + if (argv_.size() == 2) { + g_pika_server->Acl()->GetLog(-1, &res_); + return; + } + + long count = 0; + if (!strcasecmp(argv_[2].data(), "reset")) { + g_pika_server->Acl()->ResetLog(); + res().SetRes(CmdRes::kOk); + return; + } + if (!pstd::string2int(argv_[2].data(), argv_[2].size(), &count)) { + res().SetRes(CmdRes::kErrOther, fmt::format("Invalid count value: {}", argv_[2])); + return; + } + + g_pika_server->Acl()->GetLog(count, &res_); +} + +void PikaAclCmd::Save() { + auto status = g_pika_server->Acl()->SaveToFile(); + + if (status.ok()) { + res().SetRes(CmdRes::kOk); + } else { + res().SetRes(CmdRes::kErrOther, status.ToString()); + } +} + +void PikaAclCmd::SetUser() { + std::vector rule; + if (argv_.size() > 3) { + rule = std::vector(argv_.begin() + 3, argv_.end()); + } + + if (pstd::isspace(argv_[2])) { + res().SetRes(CmdRes::kErrOther, "Usernames can't contain spaces or null characters"); + return; + } + auto status = g_pika_server->Acl()->SetUser(argv_[2], rule); + if (status.ok()) { + res().SetRes(CmdRes::kOk); + return; + } + LOG(ERROR) << "ACL SETUSER modifier " + status.ToString(); + res().SetRes(CmdRes::kErrOther, "ACL SETUSER modifier " + status.ToString()); +} + +void PikaAclCmd::Users() { res().AppendStringVector(g_pika_server->Acl()->Users()); } + +void PikaAclCmd::WhoAmI() { + std::shared_ptr conn = std::dynamic_pointer_cast(GetConn()); + auto name = conn->UserName(); + + if (name.empty()) { + res().AppendString(Acl::DefaultUser); + } else { + res().AppendString(name); + } +} + +void PikaAclCmd::Help() { + if (argv_.size() > 2) { + res().SetRes(CmdRes::kWrongNum, "acl|help"); + return; + } + const std::vector info = { + "CAT []", + " List all commands that belong to , or all command categories", + " when no category is specified.", + "DELUSER [ ...]", + " Delete a list of users.", + "DRYRUN [ ...]", + " Returns whether the user can execute the given command without executing the command.", + "GETUSER ", + " Get the user's details.", + "GENPASS []", + " Generate a secure 256-bit user password. The optional `bits` argument can", + " be used to specify a different size.", + "LIST", + " Show users details in config file format.", + "LOAD", + " Reload users from the ACL file.", + "LOG [ | RESET]", + " Show the ACL log entries.", + "SAVE", + " Save the current config to the ACL file.", + "SETUSER [ ...]", + " Create or modify a user with the specified attributes.", + "USERS", + " List all the registered usernames.", + "WHOAMI", + " Return the current connection username."}; + + res().AppendStringVector(info); +} diff --git a/src/pika_admin.cc b/src/pika_admin.cc index 96db66f7b5..19c7ac63e3 100644 --- a/src/pika_admin.cc +++ b/src/pika_admin.cc @@ -5,9 +5,9 @@ #include "include/pika_admin.h" +#include #include #include -#include #include #include @@ -59,21 +59,24 @@ enum AuthResult { INVALID_CONN, }; -static AuthResult AuthenticateUser(const std::string& pwd, const std::shared_ptr& conn, - std::string& msg_role) { - std::string root_password(g_pika_conf->requirepass()); - std::string user_password(g_pika_conf->userpass()); - if (user_password.empty() && root_password.empty()) { - return AuthResult::NO_REQUIRE_PASS; +static AuthResult AuthenticateUser(const std::string& cmdName, const std::string& userName, const std::string& pwd, + const std::shared_ptr& conn, bool defaultAuth) { + if (defaultAuth) { + auto defaultUser = g_pika_server->Acl()->GetUserLock(Acl::DefaultUser); + if (defaultUser->HasFlags(static_cast(AclUserFlag::NO_PASS))) { + return AuthResult::NO_REQUIRE_PASS; + } } - if (pwd == user_password) { - msg_role = "USER"; - } - if (pwd == root_password) { - msg_role = "ROOT"; - } - if (msg_role.empty()) { + auto user = g_pika_server->Acl()->Auth(userName, pwd); + + if (!user) { + std::string cInfo; + if (auto ptr = std::dynamic_pointer_cast(conn); ptr) { + ptr->ClientInfoToString(&cInfo, cmdName); + } + g_pika_server->Acl()->AddLogEntry(static_cast(AclDeniedCmd::NO_AUTH), + static_cast(AclLogCtx::TOPLEVEL), userName, "AUTH", cInfo); return AuthResult::INVALID_PASSWORD; } @@ -82,7 +85,8 @@ static AuthResult AuthenticateUser(const std::string& pwd, const std::shared_ptr return AuthResult::INVALID_CONN; } std::shared_ptr cli_conn = std::dynamic_pointer_cast(conn); - cli_conn->auth_stat().ChecknUpdate(msg_role); + + cli_conn->DoAuth(user); return AuthResult::OK; } @@ -253,36 +257,44 @@ void AuthCmd::DoInitial() { res_.SetRes(CmdRes::kWrongNum, kCmdNameAuth); return; } - pwd_ = argv_[1]; } void AuthCmd::Do(std::shared_ptr slot) { - std::string root_password(g_pika_conf->requirepass()); - std::string user_password(g_pika_conf->userpass()); - if (user_password.empty() && root_password.empty()) { - res_.SetRes(CmdRes::kErrOther, "Client sent AUTH, but no password is set"); - return; - } - - if (pwd_ == user_password) { - res_.SetRes(CmdRes::kOk, "USER"); - } - if (pwd_ == root_password) { - res_.SetRes(CmdRes::kOk, "ROOT"); - } - if (res_.none()) { - res_.SetRes(CmdRes::kInvalidPwd); - return; - } - std::shared_ptr conn = GetConn(); if (!conn) { res_.SetRes(CmdRes::kErrOther, kCmdNamePing); LOG(WARNING) << name_ << " weak ptr is empty"; return; } - std::shared_ptr cli_conn = std::dynamic_pointer_cast(conn); - cli_conn->auth_stat().ChecknUpdate(res().raw_message()); + + std::string userName = ""; + std::string pwd = ""; + bool defaultAuth = false; + if (argv_.size() == 2) { + userName = Acl::DefaultUser; + pwd = argv_[1]; + defaultAuth = true; + } else { + userName = argv_[1]; + pwd = argv_[2]; + } + + auto authResult = AuthenticateUser(name(), userName, pwd, conn, defaultAuth); + + switch (authResult) { + case AuthResult::INVALID_CONN: + res_.SetRes(CmdRes::kErrOther, kCmdNamePing); + return; + case AuthResult::INVALID_PASSWORD: + res_.AppendContent("-WRONGPASS invalid username-password pair or user is disabled."); + return; + case AuthResult::NO_REQUIRE_PASS: + res_.SetRes(CmdRes::kErrOther, "Client sent AUTH, but no password is set"); + return; + case AuthResult::OK: + break; + } + res_.SetRes(CmdRes::kOk); } void BgsaveCmd::DoInitial() { @@ -473,7 +485,7 @@ void SelectCmd::DoInitial() { } db_name_ = "db" + argv_[1]; select_db_ = g_pika_server->GetDB(db_name_); - return ; + return; } void SelectCmd::Do(std::shared_ptr slot) { @@ -1028,8 +1040,10 @@ void InfoCmd::InfoStats(std::string& info) { tmp_stream << "total_commands_processed:" << g_pika_server->ServerQueryNum() << "\r\n"; // Network stats - tmp_stream << "total_net_input_bytes:" << g_pika_server->NetInputBytes() + g_pika_server->NetReplInputBytes() << "\r\n"; - tmp_stream << "total_net_output_bytes:" << g_pika_server->NetOutputBytes() + g_pika_server->NetReplOutputBytes() << "\r\n"; + tmp_stream << "total_net_input_bytes:" << g_pika_server->NetInputBytes() + g_pika_server->NetReplInputBytes() + << "\r\n"; + tmp_stream << "total_net_output_bytes:" << g_pika_server->NetOutputBytes() + g_pika_server->NetReplOutputBytes() + << "\r\n"; tmp_stream << "total_net_repl_input_bytes:" << g_pika_server->NetReplInputBytes() << "\r\n"; tmp_stream << "total_net_repl_output_bytes:" << g_pika_server->NetReplOutputBytes() << "\r\n"; tmp_stream << "instantaneous_input_kbps:" << g_pika_server->InstantaneousInputKbps() << "\r\n"; @@ -1519,7 +1533,7 @@ static void EncodeString(std::string* dst, const std::string& value) { dst->append(kNewLine); } -template +template static void EncodeNumber(std::string* dst, const T v) { std::string vstr = std::to_string(v); dst->append("$"); @@ -1612,18 +1626,6 @@ void ConfigCmd::ConfigGet(std::string& ret) { EncodeString(&config_body, g_pika_conf->masterauth()); } - if (pstd::stringmatch(pattern.data(), "userpass", 1) != 0) { - elements += 2; - EncodeString(&config_body, "userpass"); - EncodeString(&config_body, g_pika_conf->userpass()); - } - - if (pstd::stringmatch(pattern.data(), "userblacklist", 1) != 0) { - elements += 2; - EncodeString(&config_body, "userblacklist"); - EncodeString(&config_body, g_pika_conf->suser_blacklist()); - } - if (pstd::stringmatch(pattern.data(), "instance-mode", 1) != 0) { elements += 2; EncodeString(&config_body, "instance-mode"); @@ -2047,6 +2049,13 @@ void ConfigCmd::ConfigGet(std::string& ret) { EncodeString(&config_body, g_pika_conf->replication_id()); } + if (pstd::stringmatch(pattern.data(), "acl-pubsub-default", 1) != 0) { + elements += 2; + EncodeString(&config_body, "acl-pubsub-default"); + g_pika_conf->acl_pubsub_default() ? EncodeString(&config_body, "allchannels") + : EncodeString(&config_body, "resetchannels"); + } + std::stringstream resp; resp << "*" << std::to_string(elements) << "\r\n" << config_body; ret = resp.str(); @@ -2107,19 +2116,14 @@ void ConfigCmd::ConfigSet(std::string& ret, std::shared_ptr slot) { ret = "+OK\r\n"; } else if (set_item == "requirepass") { g_pika_conf->SetRequirePass(value); + g_pika_server->Acl()->UpdateDefaultUserPassword(value); ret = "+OK\r\n"; } else if (set_item == "masterauth") { g_pika_conf->SetMasterAuth(value); ret = "+OK\r\n"; - } else if (set_item == "userpass") { - g_pika_conf->SetUserPass(value); - ret = "+OK\r\n"; } else if (set_item == "slotmigrate") { g_pika_conf->SetSlotMigrate(value); ret = "+OK\r\n"; - } else if (set_item == "userblacklist") { - g_pika_conf->SetUserBlackList(value); - ret = "+OK\r\n"; } else if (set_item == "dump-prefix") { g_pika_conf->SetBgsavePrefix(value); ret = "+OK\r\n"; @@ -2455,7 +2459,7 @@ void ConfigCmd::ConfigSet(std::string& ret, std::shared_ptr slot) { std::set available_types = {"string", "set", "zset", "list", "hash", "bit"}; std::string type_str = value; std::vector types; - type_str.erase(remove_if(type_str.begin(), type_str.end(), isspace), type_str.end()); + type_str.erase(remove_if(type_str.begin(), type_str.end(), ::isspace), type_str.end()); pstd::StringSplit(type_str, COMMA, types); for (auto& type : types) { if (available_types.find(type) == available_types.end()) { @@ -2524,6 +2528,22 @@ void ConfigCmd::ConfigSet(std::string& ret, std::shared_ptr slot) { g_pika_conf->SetCacheLFUDecayTime(cache_lfu_decay_time); g_pika_server->ResetCacheConfig(slot); ret = "+OK\r\n"; + } else if (set_item == "acl-pubsub-default") { + std::string v(value); + pstd::StringToLower(v); + if (v != "allchannels" && v != "resetchannels") { + ret = "-ERR Invalid argument \'" + value + "\' for CONFIG SET 'acl-pubsub-default'\r\n"; + return; + } + g_pika_conf->SetAclPubsubDefault(v); + ret = "+OK\r\n"; + } else if (set_item == "acllog-max-len") { + if (pstd::string2int(value.data(), value.size(), &ival) == 0 || ival < 0) { + ret = "-ERR Invalid argument \'" + value + "\' for CONFIG SET 'acllog-max-len'\r\n"; + return; + } + g_pika_conf->SetAclLogMaxLen(static_cast(ival)); + ret = "+OK\r\n"; } else { ret = "-ERR Unsupported CONFIG parameter: " + set_item + "\r\n"; } @@ -2537,7 +2557,7 @@ void ConfigCmd::ConfigRewrite(std::string& ret) { } } -void ConfigCmd::ConfigRewriteReplicationID(std::string &ret) { +void ConfigCmd::ConfigRewriteReplicationID(std::string& ret) { if (g_pika_conf->ConfigRewriteReplicationID() != 0) { ret = "+OK\r\n"; } else { @@ -2853,7 +2873,7 @@ void HelloCmd::Do(std::shared_ptr slot) { next_arg++; if (ver < 2 || ver > 3) { - res_.SetRes(CmdRes::kErrOther, "-NOPROTO unsupported protocol version"); + res_.AppendContent("-NOPROTO unsupported protocol version"); return; } } @@ -2864,29 +2884,36 @@ void HelloCmd::Do(std::shared_ptr slot) { return; } - for (; next_arg < argv_.size(); ++next_arg) { + for (; next_arg < argv_.size(); next_arg++) { size_t more_args = argv_.size() - next_arg - 1; const std::string opt = argv_[next_arg]; - if ((strcasecmp(opt.data(), "AUTH") == 0) && (more_args != 0U)) { - const std::string pwd = argv_[next_arg + 1]; - std::string msg_role; - auto authResult = AuthenticateUser(pwd, conn, msg_role); + if ((strcasecmp(opt.data(), "AUTH") == 0) && (more_args >= 2)) { + const std::string userName = argv_[next_arg + 1]; + const std::string pwd = argv_[next_arg + 2]; + bool defaultAuth = false; + if (userName == Acl::DefaultUser) { + defaultAuth = true; + } + auto authResult = AuthenticateUser(name(), userName, pwd, conn, defaultAuth); switch (authResult) { case AuthResult::INVALID_CONN: res_.SetRes(CmdRes::kErrOther, kCmdNamePing); return; case AuthResult::INVALID_PASSWORD: - res_.SetRes(CmdRes::kInvalidPwd); + res_.AppendContent("-WRONGPASS invalid username-password pair or user is disabled."); return; case AuthResult::NO_REQUIRE_PASS: res_.SetRes(CmdRes::kErrOther, "Client sent AUTH, but no password is set"); - return; - case AuthResult::OK: + default: break; } - next_arg++; + next_arg += 2; } else if ((strcasecmp(opt.data(), "SETNAME") == 0) && (more_args != 0U)) { const std::string name = argv_[next_arg + 1]; + if (pstd::isspace(name)) { + res_.SetRes(CmdRes::kErrOther, "Client names cannot contain spaces, newlines or special characters."); + return; + } conn->set_name(name); next_arg++; } else { @@ -2968,12 +2995,12 @@ void DiskRecoveryCmd::Do(std::shared_ptr slot) { db_item.second->SetBinlogIoErrorrelieve(); std::shared_lock slot_rwl(slots_rw); // loop every slot - for (const auto &slot_item: db_item.second->GetSlots()) { + for (const auto& slot_item : db_item.second->GetSlots()) { background_errors_.clear(); slot_item.second->DbRWLockReader(); slot_item.second->db()->GetUsage(storage::PROPERTY_TYPE_ROCKSDB_BACKGROUND_ERRORS, &background_errors_); slot_item.second->DbRWUnLock(); - for (const auto &item: background_errors_) { + for (const auto& item : background_errors_) { if (item.second != 0) { rocksdb::Status s = slot_item.second->db()->GetDBByType(item.first)->Resume(); if (!s.ok()) { diff --git a/src/pika_client_conn.cc b/src/pika_client_conn.cc index 545fd87858..666e695237 100644 --- a/src/pika_client_conn.cc +++ b/src/pika_client_conn.cc @@ -5,6 +5,7 @@ #include "include/pika_client_conn.h" +#include #include #include #include @@ -20,18 +21,18 @@ #include "net/src/dispatch_thread.h" #include "net/src/worker_thread.h" +extern std::unique_ptr g_pika_conf; extern PikaServer* g_pika_server; extern std::unique_ptr g_pika_rm; extern std::unique_ptr g_pika_cmd_table_manager; - - PikaClientConn::PikaClientConn(int fd, const std::string& ip_port, net::Thread* thread, net::NetMultiplexer* mpx, const net::HandleType& handle_type, int max_conn_rbuf_size) : RedisConn(fd, ip_port, thread, mpx, handle_type, max_conn_rbuf_size), server_thread_(reinterpret_cast(thread)), current_db_(g_pika_conf->default_db()) { - auth_stat_.Init(); + // client init, set client user is default, and authenticated = false + UnAuth(g_pika_server->Acl()->GetUserLock(Acl::DefaultUser)); time_stat_.reset(new TimeStat()); } @@ -51,17 +52,12 @@ std::shared_ptr PikaClientConn::DoCmd(const PikaCmdArgsType& argv, const st c_ptr->SetResp(resp_ptr); // Check authed - // AuthCmd will set stat_ - if (!auth_stat_.IsAuthed(c_ptr)) { - c_ptr->res().SetRes(CmdRes::kErrOther, "NOAUTH Authentication required."); - return c_ptr; - } - - bool is_monitoring = g_pika_server->HasMonitorClients(); - if (is_monitoring) { - ProcessMonitor(argv); + if (AuthRequired()) { // the user is not authed, need to do auth + if (!(c_ptr->flag() & kCmdFlagsNoAuth)) { + c_ptr->res().SetRes(CmdRes::kErrOther, "NOAUTH Authentication required."); + return c_ptr; + } } - // Initial c_ptr->Initial(argv, current_db_); if (!c_ptr->res().ok()) { @@ -70,11 +66,66 @@ std::shared_ptr PikaClientConn::DoCmd(const PikaCmdArgsType& argv, const st } return c_ptr; } + + int8_t subCmdIndex = -1; + std::string errKey; + auto checkRes = user_->CheckUserPermission(c_ptr, argv, subCmdIndex, &errKey); + std::string cmdName = c_ptr->name(); + if (subCmdIndex >= 0 && checkRes == AclDeniedCmd::CMD) { + cmdName += "|" + argv[1]; + } + + std::string object; + switch (checkRes) { + case AclDeniedCmd::CMD: + c_ptr->res().SetRes(CmdRes::kNone, fmt::format("-NOPERM this user has no permissions to run the '{}' command\r\n", + pstd::StringToLower(cmdName))); + object = cmdName; + break; + case AclDeniedCmd::KEY: + c_ptr->res().SetRes(CmdRes::kNone, + "-NOPERM this user has no permissions to access one of the keys used as arguments\r\n"); + object = errKey; + break; + case AclDeniedCmd::CHANNEL: + c_ptr->res().SetRes(CmdRes::kNone, + "-NOPERM this user has no permissions to access one of the channel used as arguments\r\n"); + object = errKey; + break; + case AclDeniedCmd::NO_SUB_CMD: + c_ptr->res().SetRes(CmdRes::kErrOther, fmt::format("unknown subcommand '{}' subcommand", argv[1])); + break; + case AclDeniedCmd::NO_AUTH: + c_ptr->res().AppendContent("-NOAUTH Authentication required."); + break; + default: + break; + } + + if (checkRes == AclDeniedCmd::CMD || checkRes == AclDeniedCmd::KEY || checkRes == AclDeniedCmd::CHANNEL) { + std::string cInfo; + ClientInfoToString(&cInfo, cmdName); + int32_t context = IsInTxn() ? static_cast(AclLogCtx::MULTI) : static_cast(AclLogCtx::TOPLEVEL); + + if (checkRes == AclDeniedCmd::CMD && IsInTxn() && cmdName == kCmdNameExec) { + object = kCmdNameMulti; + } + g_pika_server->Acl()->AddLogEntry(static_cast(checkRes), context, user_->Name(), object, cInfo); + + return c_ptr; + } + if (IsInTxn() && opt != kCmdNameExec && opt != kCmdNameWatch && opt != kCmdNameDiscard && opt != kCmdNameMulti) { PushCmdToQue(c_ptr); c_ptr->res().SetRes(CmdRes::kTxnQueued); return c_ptr; } + + bool is_monitoring = g_pika_server->HasMonitorClients(); + if (is_monitoring) { + ProcessMonitor(argv); + } + g_pika_server->UpdateQueryNumAndExecCountDB(current_db_, opt, c_ptr->is_write()); // PubSub connection @@ -109,7 +160,7 @@ std::shared_ptr PikaClientConn::DoCmd(const PikaCmdArgsType& argv, const st c_ptr->res().SetRes(CmdRes::kErrOther, "Internal ERROR"); return c_ptr; } - if (g_pika_server->readonly(current_db_, cur_key.front())) { + if (g_pika_server->readonly(current_db_)) { c_ptr->res().SetRes(CmdRes::kErrOther, "Server in read-only"); return c_ptr; } @@ -176,7 +227,8 @@ void PikaClientConn::ProcessSlowlog(const PikaCmdArgsType& argv, uint64_t do_dur void PikaClientConn::ProcessMonitor(const PikaCmdArgsType& argv) { std::string monitor_message; std::string db_name = current_db_.substr(2); - monitor_message = std::to_string(1.0 * static_cast(pstd::NowMicros()) / 1000000) + " [" + db_name + " " + this->ip_port() + "]"; + monitor_message = std::to_string(1.0 * static_cast(pstd::NowMicros()) / 1000000) + " [" + db_name + " " + + this->ip_port() + "]"; for (const auto& iter : argv) { monitor_message += " " + pstd::ToRead(iter); } @@ -275,9 +327,7 @@ void PikaClientConn::TryWriteResp() { } } -void PikaClientConn::PushCmdToQue(std::shared_ptr cmd) { - txn_cmd_que_.push(cmd); -} +void PikaClientConn::PushCmdToQue(std::shared_ptr cmd) { txn_cmd_que_.push(cmd); } bool PikaClientConn::IsInTxn() { std::lock_guard lg(txn_state_mu_); @@ -319,36 +369,33 @@ void PikaClientConn::SetTxnStartState(bool is_start) { txn_state_[TxnStateBitMask::Start] = is_start; } -void PikaClientConn::ClearTxnCmdQue() { - txn_cmd_que_ = std::queue>{}; -} - +void PikaClientConn::ClearTxnCmdQue() { txn_cmd_que_ = std::queue>{}; } -void PikaClientConn::AddKeysToWatch(const std::vector &db_keys) { - for (const auto &it : db_keys) { +void PikaClientConn::AddKeysToWatch(const std::vector& db_keys) { + for (const auto& it : db_keys) { watched_db_keys_.emplace(it); } - auto dispatcher = dynamic_cast(server_thread()); + auto dispatcher = dynamic_cast(server_thread()); if (dispatcher != nullptr) { dispatcher->AddWatchKeys(watched_db_keys_, shared_from_this()); } } void PikaClientConn::RemoveWatchedKeys() { - auto dispatcher = dynamic_cast(server_thread()); + auto dispatcher = dynamic_cast(server_thread()); if (dispatcher != nullptr) { watched_db_keys_.clear(); dispatcher->RemoveWatchKeys(shared_from_this()); } } -void PikaClientConn::SetTxnFailedFromKeys(const std::vector &db_keys) { - auto dispatcher = dynamic_cast(server_thread()); +void PikaClientConn::SetTxnFailedFromKeys(const std::vector& db_keys) { + auto dispatcher = dynamic_cast(server_thread()); if (dispatcher != nullptr) { auto involved_conns = std::vector>{}; involved_conns = dispatcher->GetInvolvedTxn(db_keys); - for (auto &conn : involved_conns) { + for (auto& conn : involved_conns) { if (auto c = std::dynamic_pointer_cast(conn); c != nullptr && c.get() != this) { c->SetTxnWatchFailState(true); } @@ -357,10 +404,10 @@ void PikaClientConn::SetTxnFailedFromKeys(const std::vector &db_key } void PikaClientConn::SetAllTxnFailed() { - auto dispatcher = dynamic_cast(server_thread()); + auto dispatcher = dynamic_cast(server_thread()); if (dispatcher != nullptr) { auto involved_conns = dispatcher->GetAllTxns(); - for (auto &conn : involved_conns) { + for (auto& conn : involved_conns) { if (auto c = std::dynamic_pointer_cast(conn); c != nullptr && c.get() != this) { c->SetTxnWatchFailState(true); } @@ -369,10 +416,10 @@ void PikaClientConn::SetAllTxnFailed() { } void PikaClientConn::SetTxnFailedFromDBs(std::string db_name) { - auto dispatcher = dynamic_cast(server_thread()); + auto dispatcher = dynamic_cast(server_thread()); if (dispatcher != nullptr) { auto involved_conns = dispatcher->GetDBTxns(db_name); - for (auto &conn : involved_conns) { + for (auto& conn : involved_conns) { if (auto c = std::dynamic_pointer_cast(conn); c != nullptr && c.get() != this) { c->SetTxnWatchFailState(true); } @@ -405,54 +452,49 @@ void PikaClientConn::ExecRedisCmd(const PikaCmdArgsType& argv, std::shared_ptr> PikaClientConn::GetTxnCmdQue() { - return txn_cmd_que_; +std::queue> PikaClientConn::GetTxnCmdQue() { return txn_cmd_que_; } + +void PikaClientConn::DoAuth(const std::shared_ptr& user) { + user_ = user; + authenticated_ = true; } -// Initial permission status -void PikaClientConn::AuthStat::Init() { - // Check auth required - stat_ = g_pika_conf->userpass().empty() ? kLimitAuthed : kNoAuthed; - if (stat_ == kLimitAuthed && g_pika_conf->requirepass().empty()) { - stat_ = kAdminAuthed; - } +void PikaClientConn::UnAuth(const std::shared_ptr& user) { + user_ = user; + authenticated_ = false; } -// Check permission for current command -bool PikaClientConn::AuthStat::IsAuthed(const std::shared_ptr& cmd_ptr) { - std::string opt = cmd_ptr->name(); - if (opt == kCmdNameAuth) { - return true; - } - const std::vector& blacklist = g_pika_conf->vuser_blacklist(); - switch (stat_) { - case kNoAuthed: - return false; - case kAdminAuthed: - break; - case kLimitAuthed: - if (cmd_ptr->IsAdminRequire() || find(blacklist.begin(), blacklist.end(), opt) != blacklist.end()) { - return false; - } - break; - default: - LOG(WARNING) << "Invalid auth stat : " << static_cast(stat_); - return false; +bool PikaClientConn::IsAuthed() const { return authenticated_; } + +bool PikaClientConn::AuthRequired() const { + if (IsAuthed()) { // the user is authed, not required + return false; } - return true; -} -// Update permission status -bool PikaClientConn::AuthStat::ChecknUpdate(const std::string& message) { - // Situations to change auth status - if (message == "USER") { - stat_ = kLimitAuthed; - } else if (message == "ROOT") { - stat_ = kAdminAuthed; - } else { + if (user_->HasFlags(static_cast(AclUserFlag::NO_PASS))) { // the user is no password return false; } - return true; + + return user_->HasFlags(static_cast(AclUserFlag::DISABLED)); // user disabled +} +std::string PikaClientConn::UserName() const { return user_->Name(); } + +void PikaClientConn::ClientInfoToString(std::string* info, const std::string& cmdName) { + uint64_t age = pstd::NowMicros() - last_interaction().tv_usec; + + std::string flags; + g_pika_server->ClientIsMonitor(std::dynamic_pointer_cast(shared_from_this())) ? flags.append("O") + : flags.append("S"); + if (IsPubSub()) { + flags.append("P"); + } + + info->append(fmt::format( + "id={} addr={} name={} age={} idle={} flags={} db={} sub={} psub={} multi={} " + "cmd={} user={} resp=2", + fd(), ip_port(), name(), age, age / 1000000, flags, GetCurrentTable(), + IsPubSub() ? g_pika_server->ClientPubSubChannelSize(shared_from_this()) : 0, + IsPubSub() ? g_pika_server->ClientPubSubChannelPatternSize(shared_from_this()) : 0, -1, cmdName, user_->Name())); } // compare addr in ClientInfo diff --git a/src/pika_cmd_table_manager.cc b/src/pika_cmd_table_manager.cc index a7fe54b285..04d14aad62 100644 --- a/src/pika_cmd_table_manager.cc +++ b/src/pika_cmd_table_manager.cc @@ -8,6 +8,7 @@ #include #include +#include "include/acl.h" #include "include/pika_conf.h" #include "pstd/include/pstd_mutex.h" @@ -16,7 +17,33 @@ extern std::unique_ptr g_pika_conf; PikaCmdTableManager::PikaCmdTableManager() { cmds_ = std::make_unique(); cmds_->reserve(300); - InitCmdTable(cmds_.get()); +} + +void PikaCmdTableManager::InitCmdTable(void) { + ::InitCmdTable(cmds_.get()); + + for (const auto& cmd : *cmds_) { + if (cmd.second->flag() & kCmdFlagsWrite) { + cmd.second->AddAclCategory(static_cast(AclCategory::WRITE)); + } + if (cmd.second->flag() & kCmdFlagsRead && + !(cmd.second->AclCategory() & static_cast(AclCategory::SCRIPTING))) { + cmd.second->AddAclCategory(static_cast(AclCategory::READ)); + } + if (cmd.second->flag() & kCmdFlagsAdmin) { + cmd.second->AddAclCategory(static_cast(AclCategory::ADMIN) | + static_cast(AclCategory::DANGEROUS)); + } + if (cmd.second->flag() & kCmdFlagsPubSub) { + cmd.second->AddAclCategory(static_cast(AclCategory::PUBSUB)); + } + if (cmd.second->flag() & kCmdFlagsFast) { + cmd.second->AddAclCategory(static_cast(AclCategory::FAST)); + } + if (cmd.second->flag() & kCmdFlagsSlow) { + cmd.second->AddAclCategory(static_cast(AclCategory::SLOW)); + } + } CommandStatistics statistics; for (auto& iter : *cmds_) { @@ -41,9 +68,9 @@ std::shared_ptr PikaCmdTableManager::NewCommand(const std::string& opt) { return nullptr; } -CmdTable* PikaCmdTableManager::GetCmdTable() { - return cmds_.get(); -} +CmdTable* PikaCmdTableManager::GetCmdTable() { return cmds_.get(); } + +uint32_t PikaCmdTableManager::GetCmdId() { return ++cmdId_; } bool PikaCmdTableManager::CheckCurrentThreadDistributionMapExist(const std::thread::id& tid) { std::shared_lock l(map_protector_); @@ -69,3 +96,13 @@ uint32_t PikaCmdTableManager::DistributeKey(const std::string& key, uint32_t slo } bool PikaCmdTableManager::CmdExist(const std::string& cmd) const { return cmds_->find(cmd) != cmds_->end(); } + +std::vector PikaCmdTableManager::GetAclCategoryCmdNames(uint32_t flag) { + std::vector result; + for (const auto& item : (*cmds_)) { + if (item.second->AclCategory() & flag) { + result.emplace_back(item.first); + } + } + return result; +} diff --git a/src/pika_command.cc b/src/pika_command.cc index 812c86e11c..425c2bac73 100644 --- a/src/pika_command.cc +++ b/src/pika_command.cc @@ -7,6 +7,7 @@ #include #include +#include "include/pika_acl.h" #include "include/pika_admin.h" #include "include/pika_bit.h" #include "include/pika_cmd_table_manager.h" @@ -21,8 +22,8 @@ #include "include/pika_server.h" #include "include/pika_set.h" #include "include/pika_slot_command.h" -#include "include/pika_transaction.h" #include "include/pika_stream.h" +#include "include/pika_transaction.h" #include "include/pika_zset.h" #include "pstd_defer.h" #include "include/pika_cache.h" @@ -37,23 +38,23 @@ void InitCmdTable(CmdTable* cmd_table) { // Admin ////Slaveof std::unique_ptr slaveofptr = - std::make_unique(kCmdNameSlaveof, -3, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlaveof, -3, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlaveof, std::move(slaveofptr))); std::unique_ptr dbslaveofptr = - std::make_unique(kCmdNameDbSlaveof, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameDbSlaveof, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameDbSlaveof, std::move(dbslaveofptr))); std::unique_ptr authptr = - std::make_unique(kCmdNameAuth, 2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsNoAuth ); + std::make_unique(kCmdNameAuth, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsNoAuth | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameAuth, std::move(authptr))); std::unique_ptr bgsaveptr = std::make_unique( - kCmdNameBgsave, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSuspend ); + kCmdNameBgsave, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSuspend | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBgsave, std::move(bgsaveptr))); std::unique_ptr compactptr = - std::make_unique(kCmdNameCompact, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameCompact, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameCompact, std::move(compactptr))); std::unique_ptr compactrangeptr = std::make_unique(kCmdNameCompactRange, 5, kCmdFlagsRead | kCmdFlagsAdmin); @@ -63,51 +64,51 @@ void InitCmdTable(CmdTable* cmd_table) { cmd_table->insert(std::pair>(kCmdNamePurgelogsto, std::move(purgelogsto))); std::unique_ptr pingptr = - std::make_unique(kCmdNamePing, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNamePing, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePing, std::move(pingptr))); std::unique_ptr helloptr = - std::make_unique(kCmdNameHello, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsNoAuth ); + std::make_unique(kCmdNameHello, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsNoAuth | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHello, std::move(helloptr))); std::unique_ptr selectptr = - std::make_unique(kCmdNameSelect, 2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSelect, 2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSelect, std::move(selectptr))); std::unique_ptr flushallptr = std::make_unique( - kCmdNameFlushall, 1, kCmdFlagsWrite | kCmdFlagsSuspend | kCmdFlagsAdmin | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + kCmdNameFlushall, 1, kCmdFlagsWrite | kCmdFlagsSuspend | kCmdFlagsAdmin | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameFlushall, std::move(flushallptr))); std::unique_ptr flushdbptr = std::make_unique( - kCmdNameFlushdb, -1, kCmdFlagsWrite | kCmdFlagsSuspend | kCmdFlagsAdmin | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + kCmdNameFlushdb, -1, kCmdFlagsWrite | kCmdFlagsSuspend | kCmdFlagsAdmin | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameFlushdb, std::move(flushdbptr))); std::unique_ptr clientptr = - std::make_unique(kCmdNameClient, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameClient, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameClient, std::move(clientptr))); std::unique_ptr shutdownptr = std::make_unique( - kCmdNameShutdown, 1, kCmdFlagsRead | kCmdFlagsLocal | kCmdFlagsAdmin ); + kCmdNameShutdown, 1, kCmdFlagsRead | kCmdFlagsLocal | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameShutdown, std::move(shutdownptr))); std::unique_ptr infoptr = - std::make_unique(kCmdNameInfo, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameInfo, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameInfo, std::move(infoptr))); std::unique_ptr configptr = - std::make_unique(kCmdNameConfig, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameConfig, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameConfig, std::move(configptr))); std::unique_ptr monitorptr = - std::make_unique(kCmdNameMonitor, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameMonitor, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameMonitor, std::move(monitorptr))); std::unique_ptr dbsizeptr = - std::make_unique(kCmdNameDbsize, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameDbsize, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameDbsize, std::move(dbsizeptr))); std::unique_ptr timeptr = - std::make_unique(kCmdNameTime, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameTime, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameTime, std::move(timeptr))); std::unique_ptr delbackupptr = @@ -115,15 +116,15 @@ void InitCmdTable(CmdTable* cmd_table) { cmd_table->insert(std::pair>(kCmdNameDelbackup, std::move(delbackupptr))); std::unique_ptr echoptr = - std::make_unique(kCmdNameEcho, 2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameEcho, 2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameEcho, std::move(echoptr))); std::unique_ptr scandbptr = - std::make_unique(kCmdNameScandb, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameScandb, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameScandb, std::move(scandbptr))); std::unique_ptr slowlogptr = - std::make_unique(kCmdNameSlowlog, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlowlog, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlowlog, std::move(slowlogptr))); std::unique_ptr paddingptr = std::make_unique(kCmdNamePadding, 2, kCmdFlagsWrite | kCmdFlagsAdmin); @@ -138,15 +139,15 @@ void InitCmdTable(CmdTable* cmd_table) { cmd_table->insert(std::pair>(kCmdDummy, std::move(dummyptr))); std::unique_ptr quitptr = - std::make_unique(kCmdNameQuit, 1, kCmdFlagsRead | kCmdFlagsNoAuth ); + std::make_unique(kCmdNameQuit, 1, kCmdFlagsRead | kCmdFlagsNoAuth | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameQuit, std::move(quitptr))); std::unique_ptr diskrecoveryptr = - std::make_unique(kCmdNameDiskRecovery, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameDiskRecovery, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameDiskRecovery, std::move(diskrecoveryptr))); std::unique_ptr clearreplicationidptr = std::make_unique( - kCmdNameClearReplicationID, 1, kCmdFlagsWrite | kCmdFlagsAdmin ); + kCmdNameClearReplicationID, 1, kCmdFlagsWrite | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameClearReplicationID, std::move(clearreplicationidptr))); std::unique_ptr disablewalptr = std::make_unique(kCmdNameDisableWal, 2, kCmdFlagsAdmin); @@ -155,669 +156,672 @@ void InitCmdTable(CmdTable* cmd_table) { cmd_table->insert(std::pair>(kCmdNameCache, std::move(cacheptr))); std::unique_ptr clearcacheptr = std::make_unique(kCmdNameClearCache, 1, kCmdFlagsAdmin | kCmdFlagsWrite); cmd_table->insert(std::pair>(kCmdNameClearCache, std::move(clearcacheptr))); - std::unique_ptr lastsaveptr = std::make_unique(kCmdNameLastSave, 1, kCmdFlagsAdmin | kCmdFlagsRead); + std::unique_ptr lastsaveptr = std::make_unique(kCmdNameLastSave, 1, kCmdFlagsAdmin | kCmdFlagsRead | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameLastSave, std::move(lastsaveptr))); #ifdef WITH_COMMAND_DOCS std::unique_ptr commandptr = - std::make_unique(kCmdNameCommand, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameCommand, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameCommand, std::move(commandptr))); #endif // Slots related std::unique_ptr slotsinfoptr = - std::make_unique(kCmdNameSlotsInfo, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsInfo, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsInfo, std::move(slotsinfoptr))); std::unique_ptr slotmgrttagslotasyncptr = std::make_unique( - kCmdNameSlotsMgrtTagSlotAsync, 8, kCmdFlagsRead | kCmdFlagsAdmin ); + kCmdNameSlotsMgrtTagSlotAsync, 8, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtTagSlotAsync, std::move(slotmgrttagslotasyncptr))); std::unique_ptr slotmgrtasyncstatus = std::make_unique( - kCmdNameSlotsMgrtAsyncStatus, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + kCmdNameSlotsMgrtAsyncStatus, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtAsyncStatus, std::move(slotmgrtasyncstatus))); std::unique_ptr slotmgrtasynccancel = std::make_unique( - kCmdNameSlotsMgrtAsyncCancel, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + kCmdNameSlotsMgrtAsyncCancel, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtAsyncCancel, std::move(slotmgrtasynccancel))); std::unique_ptr slotmgrttagoneptr = - std::make_unique(kCmdNameSlotsMgrtTagOne, 5, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsMgrtTagOne, 5, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtTagOne, std::move(slotmgrttagoneptr))); std::unique_ptr slotmgrtoneptr = - std::make_unique(kCmdNameSlotsMgrtOne, 5, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsMgrtOne, 5, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsMgrtOne, std::move(slotmgrtoneptr))); std::unique_ptr slotmgrttagslotptr = std::make_unique( - kCmdNameSlotsMgrtTagSlot, 5, kCmdFlagsRead | kCmdFlagsAdmin ); + kCmdNameSlotsMgrtTagSlot, 5, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtTagSlot, std::move(slotmgrttagslotptr))); std::unique_ptr slotmgrttagslottagptr = - std::make_unique(kCmdNameSlotsMgrtSlot, 5, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsMgrtSlot, 5, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtSlot, std::move(slotmgrttagslottagptr))); std::unique_ptr slotsdelptr = - std::make_unique(kCmdNameSlotsDel, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsDel, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsDel, std::move(slotsdelptr))); std::unique_ptr slotshashkeyptr = - std::make_unique(kCmdNameSlotsHashKey, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsHashKey, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsHashKey, std::move(slotshashkeyptr))); std::unique_ptr slotsscanptr = - std::make_unique(kCmdNameSlotsScan, -3, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsScan, -3, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsScan, std::move(slotsscanptr))); std::unique_ptr slotsmgrtexecwrapper = std::make_unique( - kCmdNameSlotsMgrtExecWrapper, -3, kCmdFlagsWrite | kCmdFlagsAdmin ); + kCmdNameSlotsMgrtExecWrapper, -3, kCmdFlagsWrite | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsMgrtExecWrapper, std::move(slotsmgrtexecwrapper))); std::unique_ptr slotsreloadptr = - std::make_unique(kCmdNameSlotsReload, 1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsReload, 1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsReload, std::move(slotsreloadptr))); std::unique_ptr slotsreloadoffptr = - std::make_unique(kCmdNameSlotsReloadOff, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsReloadOff, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsReloadOff, std::move(slotsreloadoffptr))); std::unique_ptr slotscleanupptr = - std::make_unique(kCmdNameSlotsCleanup, -2, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsCleanup, -2, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSlotsCleanup, std::move(slotscleanupptr))); std::unique_ptr slotscleanupoffptr = - std::make_unique(kCmdNameSlotsCleanupOff, -1, kCmdFlagsRead | kCmdFlagsAdmin ); + std::make_unique(kCmdNameSlotsCleanupOff, -1, kCmdFlagsRead | kCmdFlagsAdmin | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameSlotsCleanupOff, std::move(slotscleanupoffptr))); // Kv ////SetCmd std::unique_ptr setptr = - std::make_unique(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSet, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSet, std::move(setptr))); ////GetCmd std::unique_ptr getptr = - std::make_unique(kCmdNameGet, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache); + std::make_unique(kCmdNameGet, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGet, std::move(getptr))); ////DelCmd std::unique_ptr delptr = - std::make_unique(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameDel, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameDel, std::move(delptr))); std::unique_ptr Unlinkptr = - std::make_unique(kCmdNameUnlink, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsOperateKey ); + std::make_unique(kCmdNameUnlink, -2, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameUnlink, std::move(Unlinkptr))); ////IncrCmd std::unique_ptr incrptr = - std::make_unique(kCmdNameIncr, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameIncr, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameIncr, std::move(incrptr))); ////IncrbyCmd std::unique_ptr incrbyptr = std::make_unique( - kCmdNameIncrby, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameIncrby, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameIncrby, std::move(incrbyptr))); ////IncrbyfloatCmd std::unique_ptr incrbyfloatptr = std::make_unique( - kCmdNameIncrbyfloat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameIncrbyfloat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameIncrbyfloat, std::move(incrbyfloatptr))); ////DecrCmd std::unique_ptr decrptr = - std::make_unique(kCmdNameDecr, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameDecr, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameDecr, std::move(decrptr))); ////DecrbyCmd std::unique_ptr decrbyptr = std::make_unique( - kCmdNameDecrby, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameDecrby, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameDecrby, std::move(decrbyptr))); ////GetsetCmd std::unique_ptr getsetptr = std::make_unique( - kCmdNameGetset, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameGetset, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameGetset, std::move(getsetptr))); ////AppendCmd std::unique_ptr appendptr = std::make_unique( - kCmdNameAppend, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameAppend, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameAppend, std::move(appendptr))); ////MgetCmd std::unique_ptr mgetptr = - std::make_unique(kCmdNameMget, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache); + std::make_unique(kCmdNameMget, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameMget, std::move(mgetptr))); ////KeysCmd std::unique_ptr keysptr = - std::make_unique(kCmdNameKeys, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey ); + std::make_unique(kCmdNameKeys, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameKeys, std::move(keysptr))); ////SetnxCmd std::unique_ptr setnxptr = - std::make_unique(kCmdNameSetnx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv ); + std::make_unique(kCmdNameSetnx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSetnx, std::move(setnxptr))); ////SetexCmd std::unique_ptr setexptr = - std::make_unique(kCmdNameSetex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSetex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSetex, std::move(setexptr))); ////PsetexCmd std::unique_ptr psetexptr = - std::make_unique(kCmdNamePsetex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNamePsetex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePsetex, std::move(psetexptr))); ////DelvxCmd std::unique_ptr delvxptr = - std::make_unique(kCmdNameDelvx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv ); + std::make_unique(kCmdNameDelvx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameDelvx, std::move(delvxptr))); ////MSetCmd std::unique_ptr msetptr = - std::make_unique(kCmdNameMset, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameMset, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameMset, std::move(msetptr))); ////MSetnxCmd std::unique_ptr msetnxptr = std::make_unique( - kCmdNameMsetnx, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv ); + kCmdNameMsetnx, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsKv | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameMsetnx, std::move(msetnxptr))); ////GetrangeCmd std::unique_ptr getrangeptr = std::make_unique( - kCmdNameGetrange, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache); + kCmdNameGetrange, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGetrange, std::move(getrangeptr))); ////SetrangeCmd std::unique_ptr setrangeptr = std::make_unique( - kCmdNameSetrange, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameSetrange, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSetrange, std::move(setrangeptr))); ////StrlenCmd std::unique_ptr strlenptr = - std::make_unique(kCmdNameStrlen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache); + std::make_unique(kCmdNameStrlen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameStrlen, std::move(strlenptr))); ////ExistsCmd std::unique_ptr existsptr = - std::make_unique(kCmdNameExists, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameExists, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameExists, std::move(existsptr))); ////ExpireCmd std::unique_ptr expireptr = std::make_unique( - kCmdNameExpire, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameExpire, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameExpire, std::move(expireptr))); ////PexpireCmd std::unique_ptr pexpireptr = std::make_unique( - kCmdNamePexpire, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNamePexpire, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePexpire, std::move(pexpireptr))); ////ExpireatCmd std::unique_ptr expireatptr = - std::make_unique(kCmdNameExpireat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameExpireat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameExpireat, std::move(expireatptr))); ////PexpireatCmd std::unique_ptr pexpireatptr = - std::make_unique(kCmdNamePexpireat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNamePexpireat, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePexpireat, std::move(pexpireatptr))); ////TtlCmd std::unique_ptr ttlptr = - std::make_unique(kCmdNameTtl, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameTtl, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameTtl, std::move(ttlptr))); ////PttlCmd std::unique_ptr pttlptr = - std::make_unique(kCmdNamePttl, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNamePttl, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePttl, std::move(pttlptr))); ////PersistCmd std::unique_ptr persistptr = - std::make_unique(kCmdNamePersist, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNamePersist, 2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePersist, std::move(persistptr))); ////TypeCmd std::unique_ptr typeptr = - std::make_unique(kCmdNameType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameType, std::move(typeptr))); ////PTypeCmd std::unique_ptr pTypeptr = - std::make_unique(kCmdNamePType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey); + std::make_unique(kCmdNamePType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePType, std::move(pTypeptr))); ////ScanCmd std::unique_ptr scanptr = - std::make_unique(kCmdNameScan, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey ); + std::make_unique(kCmdNameScan, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameScan, std::move(scanptr))); ////ScanxCmd std::unique_ptr scanxptr = - std::make_unique(kCmdNameScanx, -3, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey ); + std::make_unique(kCmdNameScanx, -3, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsOperateKey | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameScanx, std::move(scanxptr))); ////PKSetexAtCmd std::unique_ptr pksetexatptr = std::make_unique( - kCmdNamePKSetexAt, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv ); + kCmdNamePKSetexAt, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsKv | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePKSetexAt, std::move(pksetexatptr))); ////PKScanRange std::unique_ptr pkscanrangeptr = std::make_unique( - kCmdNamePKScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey ); + kCmdNamePKScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePKScanRange, std::move(pkscanrangeptr))); ////PKRScanRange std::unique_ptr pkrscanrangeptr = std::make_unique( - kCmdNamePKRScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey ); + kCmdNamePKRScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsOperateKey | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePKRScanRange, std::move(pkrscanrangeptr))); // Hash ////HDelCmd std::unique_ptr hdelptr = - std::make_unique(kCmdNameHDel, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHDel, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHDel, std::move(hdelptr))); ////HSetCmd std::unique_ptr hsetptr = - std::make_unique(kCmdNameHSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHSet, std::move(hsetptr))); ////HGetCmd std::unique_ptr hgetptr = - std::make_unique(kCmdNameHGet, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHGet, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHGet, std::move(hgetptr))); ////HGetallCmd std::unique_ptr hgetallptr = - std::make_unique(kCmdNameHGetall, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash); + std::make_unique(kCmdNameHGetall, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameHGetall, std::move(hgetallptr))); ////HExistsCmd std::unique_ptr hexistsptr = - std::make_unique(kCmdNameHExists, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHExists, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHExists, std::move(hexistsptr))); ////HIncrbyCmd std::unique_ptr hincrbyptr = - std::make_unique(kCmdNameHIncrby, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHIncrby, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHIncrby, std::move(hincrbyptr))); ////HIncrbyfloatCmd std::unique_ptr hincrbyfloatptr = - std::make_unique(kCmdNameHIncrbyfloat, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHIncrbyfloat, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHIncrbyfloat, std::move(hincrbyfloatptr))); ////HKeysCmd std::unique_ptr hkeysptr = - std::make_unique(kCmdNameHKeys, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHKeys, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHKeys, std::move(hkeysptr))); ////HLenCmd std::unique_ptr hlenptr = - std::make_unique(kCmdNameHLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHLen, std::move(hlenptr))); ////HMgetCmd std::unique_ptr hmgetptr = - std::make_unique(kCmdNameHMget, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHMget, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHMget, std::move(hmgetptr))); ////HMsetCmd std::unique_ptr hmsetptr = - std::make_unique(kCmdNameHMset, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHMset, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHMset, std::move(hmsetptr))); ////HSetnxCmd std::unique_ptr hsetnxptr = - std::make_unique(kCmdNameHSetnx, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB); + std::make_unique(kCmdNameHSetnx, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHSetnx, std::move(hsetnxptr))); ////HStrlenCmd std::unique_ptr hstrlenptr = - std::make_unique(kCmdNameHStrlen, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameHStrlen, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsUpdateCache | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameHStrlen, std::move(hstrlenptr))); ////HValsCmd std::unique_ptr hvalsptr = - std::make_unique(kCmdNameHVals, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash); + std::make_unique(kCmdNameHVals, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameHVals, std::move(hvalsptr))); ////HScanCmd std::unique_ptr hscanptr = std::make_unique( - kCmdNameHScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash ); + kCmdNameHScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameHScan, std::move(hscanptr))); ////HScanxCmd std::unique_ptr hscanxptr = std::make_unique( - kCmdNameHScanx, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash ); + kCmdNameHScanx, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameHScanx, std::move(hscanxptr))); ////PKHScanRange std::unique_ptr pkhscanrangeptr = std::make_unique( - kCmdNamePKHScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash ); + kCmdNamePKHScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePKHScanRange, std::move(pkhscanrangeptr))); ////PKHRScanRange std::unique_ptr pkhrscanrangeptr = std::make_unique( - kCmdNamePKHRScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash ); + kCmdNamePKHRScanRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsHash | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePKHRScanRange, std::move(pkhrscanrangeptr))); // List std::unique_ptr lindexptr = - std::make_unique(kCmdNameLIndex, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLIndex, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLIndex, std::move(lindexptr))); std::unique_ptr linsertptr = - std::make_unique(kCmdNameLInsert, 5, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLInsert, 5, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLInsert, std::move(linsertptr))); std::unique_ptr llenptr = - std::make_unique(kCmdNameLLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameLLen, std::move(llenptr))); std::unique_ptr blpopptr = std::make_unique( - kCmdNameBLPop, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList ); + kCmdNameBLPop, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBLPop, std::move(blpopptr))); std::unique_ptr lpopptr = - std::make_unique(kCmdNameLPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameLPop, std::move(lpopptr))); std::unique_ptr lpushptr = std::make_unique( - kCmdNameLPush, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameLPush, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameLPush, std::move(lpushptr))); - std::unique_ptr lpushxptr = - std::make_unique(kCmdNameLPushx, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::unique_ptr lpushxptr = std::make_unique(kCmdNameLPushx, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameLPushx, std::move(lpushxptr))); std::unique_ptr lrangeptr = std::make_unique( - kCmdNameLRange, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + kCmdNameLRange, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLRange, std::move(lrangeptr))); std::unique_ptr lremptr = - std::make_unique(kCmdNameLRem, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLRem, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLRem, std::move(lremptr))); std::unique_ptr lsetptr = - std::make_unique(kCmdNameLSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLSet, std::move(lsetptr))); std::unique_ptr ltrimptr = - std::make_unique(kCmdNameLTrim, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameLTrim, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameLTrim, std::move(ltrimptr))); std::unique_ptr brpopptr = std::make_unique( - kCmdNameBRpop, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList ); + kCmdNameBRpop, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBRpop, std::move(brpopptr))); std::unique_ptr rpopptr = - std::make_unique(kCmdNameRPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameRPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameRPop, std::move(rpopptr))); std::unique_ptr rpoplpushptr = std::make_unique( - kCmdNameRPopLPush, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList ); + kCmdNameRPopLPush, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameRPopLPush, std::move(rpoplpushptr))); std::unique_ptr rpushptr = - std::make_unique(kCmdNameRPush, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot |kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameRPush, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot |kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameRPush, std::move(rpushptr))); std::unique_ptr rpushxptr = std::make_unique(kCmdNameRPushx, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList); - std::make_unique(kCmdNameRPushx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameRPushx, 3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsList | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameRPushx, std::move(rpushxptr))); // Zset ////ZAddCmd std::unique_ptr zaddptr = - std::make_unique(kCmdNameZAdd, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZAdd, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZAdd, std::move(zaddptr))); ////ZCardCmd std::unique_ptr zcardptr = - std::make_unique(kCmdNameZCard, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameZCard, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZCard, std::move(zcardptr))); ////ZScanCmd std::unique_ptr zscanptr = std::make_unique( - kCmdNameZScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset ); + kCmdNameZScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZScan, std::move(zscanptr))); ////ZIncrbyCmd std::unique_ptr zincrbyptr = - std::make_unique(kCmdNameZIncrby, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache) ; + std::make_unique(kCmdNameZIncrby, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast) ; cmd_table->insert(std::pair>(kCmdNameZIncrby, std::move(zincrbyptr))); ////ZRangeCmd std::unique_ptr zrangeptr = - std::make_unique(kCmdNameZRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRange, std::move(zrangeptr))); ////ZRevrangeCmd std::unique_ptr zrevrangeptr = - std::make_unique(kCmdNameZRevrange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZRevrange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRevrange, std::move(zrevrangeptr))); ////ZRangebyscoreCmd std::unique_ptr zrangebyscoreptr = std::make_unique( - kCmdNameZRangebyscore, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset); + kCmdNameZRangebyscore, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRangebyscore, std::move(zrangebyscoreptr))); ////ZRevrangebyscoreCmd std::unique_ptr zrevrangebyscoreptr = std::make_unique( - kCmdNameZRevrangebyscore, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset); + kCmdNameZRevrangebyscore, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameZRevrangebyscore, std::move(zrevrangebyscoreptr))); ////ZCountCmd std::unique_ptr zcountptr = - std::make_unique(kCmdNameZCount, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZCount, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZCount, std::move(zcountptr))); ////ZRemCmd std::unique_ptr zremptr = - std::make_unique(kCmdNameZRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZRem, std::move(zremptr))); ////ZUnionstoreCmd std::unique_ptr zunionstoreptr = - std::make_unique(kCmdNameZUnionstore, -4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZUnionstore, -4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZUnionstore, std::move(zunionstoreptr))); ////ZInterstoreCmd std::unique_ptr zinterstoreptr = - std::make_unique(kCmdNameZInterstore, -4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZInterstore, -4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZInterstore, std::move(zinterstoreptr))); ////ZRankCmd std::unique_ptr zrankptr = - std::make_unique(kCmdNameZRank, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZRank, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZRank, std::move(zrankptr))); ////ZRevrankCmd std::unique_ptr zrevrankptr = - std::make_unique(kCmdNameZRevrank, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameZRevrank, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZRevrank, std::move(zrevrankptr))); ////ZScoreCmd std::unique_ptr zscoreptr = - std::make_unique(kCmdNameZScore, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache); + std::make_unique(kCmdNameZScore, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZScore, std::move(zscoreptr))); ////ZRangebylexCmd std::unique_ptr zrangebylexptr = - std::make_unique(kCmdNameZRangebylex, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset); + std::make_unique(kCmdNameZRangebylex, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRangebylex, std::move(zrangebylexptr))); ////ZRevrangebylexCmd std::unique_ptr zrevrangebylexptr = std::make_unique( - kCmdNameZRevrangebylex, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset); + kCmdNameZRevrangebylex, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRevrangebylex, std::move(zrevrangebylexptr))); ////ZLexcountCmd std::unique_ptr zlexcountptr = - std::make_unique(kCmdNameZLexcount, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset); + std::make_unique(kCmdNameZLexcount, 4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZLexcount, std::move(zlexcountptr))); ////ZRemrangebyrankCmd std::unique_ptr zremrangebyrankptr = std::make_unique( - kCmdNameZRemrangebyrank, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameZRemrangebyrank, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameZRemrangebyrank, std::move(zremrangebyrankptr))); ////ZRemrangebyscoreCmd std::unique_ptr zremrangebyscoreptr = std::make_unique( - kCmdNameZRemrangebyscore, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameZRemrangebyscore, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameZRemrangebyscore, std::move(zremrangebyscoreptr))); ////ZRemrangebylexCmd std::unique_ptr zremrangebylexptr = std::make_unique( - kCmdNameZRemrangebylex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + kCmdNameZRemrangebylex, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameZRemrangebylex, std::move(zremrangebylexptr))); ////ZPopmax std::unique_ptr zpopmaxptr = std::make_unique( - kCmdNameZPopmax, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset ); + kCmdNameZPopmax, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZPopmax, std::move(zpopmaxptr))); ////ZPopmin std::unique_ptr zpopminptr = std::make_unique( - kCmdNameZPopmin, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset ); + kCmdNameZPopmin, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsZset | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameZPopmin, std::move(zpopminptr))); // Set ////SAddCmd std::unique_ptr saddptr = - std::make_unique(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSAdd, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSAdd, std::move(saddptr))); ////SPopCmd std::unique_ptr spopptr = - std::make_unique(kCmdNameSPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSPop, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSPop, std::move(spopptr))); ////SCardCmd std::unique_ptr scardptr = - std::make_unique(kCmdNameSCard, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSCard, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSCard, std::move(scardptr))); ////SMembersCmd std::unique_ptr smembersptr = - std::make_unique(kCmdNameSMembers, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSMembers, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSMembers, std::move(smembersptr))); ////SScanCmd std::unique_ptr sscanptr = - std::make_unique(kCmdNameSScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet ); + std::make_unique(kCmdNameSScan, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSScan, std::move(sscanptr))); ////SRemCmd std::unique_ptr sremptr = - std::make_unique(kCmdNameSRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSRem, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSRem, std::move(sremptr))); ////SUnionCmd std::unique_ptr sunionptr = std::make_unique( - kCmdNameSUnion, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet ); + kCmdNameSUnion, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSUnion, std::move(sunionptr))); ////SUnionstoreCmd std::unique_ptr sunionstoreptr = - std::make_unique(kCmdNameSUnionstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSUnionstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSUnionstore, std::move(sunionstoreptr))); ////SInterCmd std::unique_ptr sinterptr = std::make_unique( - kCmdNameSInter, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet ); + kCmdNameSInter, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSInter, std::move(sinterptr))); ////SInterstoreCmd std::unique_ptr sinterstoreptr = - std::make_unique(kCmdNameSInterstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSInterstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSInterstore, std::move(sinterstoreptr))); ////SIsmemberCmd std::unique_ptr sismemberptr = - std::make_unique(kCmdNameSIsmember, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSIsmember, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSIsmember, std::move(sismemberptr))); ////SDiffCmd std::unique_ptr sdiffptr = - std::make_unique(kCmdNameSDiff, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet ); + std::make_unique(kCmdNameSDiff, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSDiff, std::move(sdiffptr))); ////SDiffstoreCmd std::unique_ptr sdiffstoreptr = - std::make_unique(kCmdNameSDiffstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSDiffstore, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet |kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSDiffstore, std::move(sdiffstoreptr))); ////SMoveCmd std::unique_ptr smoveptr = - std::make_unique(kCmdNameSMove, 4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSMove, 4, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSet | kCmdFlagsDoThroughDB | kCmdFlagsUpdateCache | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameSMove, std::move(smoveptr))); ////SRandmemberCmd std::unique_ptr srandmemberptr = - std::make_unique(kCmdNameSRandmember, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet|kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache); + std::make_unique(kCmdNameSRandmember, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsSet|kCmdFlagsDoThroughDB | kCmdFlagsReadCache | kCmdFlagsUpdateCache | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSRandmember, std::move(srandmemberptr))); // BitMap ////bitsetCmd std::unique_ptr bitsetptr = - std::make_unique(kCmdNameBitSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsBit); + std::make_unique(kCmdNameBitSet, 4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsBit | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBitSet, std::move(bitsetptr))); ////bitgetCmd std::unique_ptr bitgetptr = - std::make_unique(kCmdNameBitGet, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit); + std::make_unique(kCmdNameBitGet, 3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBitGet, std::move(bitgetptr))); ////bitcountCmd std::unique_ptr bitcountptr = - std::make_unique(kCmdNameBitCount, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit); + std::make_unique(kCmdNameBitCount, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBitCount, std::move(bitcountptr))); ////bitposCmd std::unique_ptr bitposptr = - std::make_unique(kCmdNameBitPos, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit); + std::make_unique(kCmdNameBitPos, -3, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsBit | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBitPos, std::move(bitposptr))); ////bitopCmd std::unique_ptr bitopptr = - std::make_unique(kCmdNameBitOp, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsBit); + std::make_unique(kCmdNameBitOp, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsBit | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameBitOp, std::move(bitopptr))); // HyperLogLog ////pfaddCmd std::unique_ptr pfaddptr = std::make_unique( - kCmdNamePfAdd, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHyperLogLog ); + kCmdNamePfAdd, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsHyperLogLog | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePfAdd, std::move(pfaddptr))); ////pfcountCmd std::unique_ptr pfcountptr = std::make_unique( - kCmdNamePfCount, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsHyperLogLog ); + kCmdNamePfCount, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsHyperLogLog | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePfCount, std::move(pfcountptr))); ////pfmergeCmd std::unique_ptr pfmergeptr = std::make_unique( - kCmdNamePfMerge, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsHyperLogLog ); + kCmdNamePfMerge, -3, kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsHyperLogLog | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePfMerge, std::move(pfmergeptr))); // GEO ////GepAdd std::unique_ptr geoaddptr = std::make_unique( - kCmdNameGeoAdd, -5, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsGeo ); + kCmdNameGeoAdd, -5, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGeoAdd, std::move(geoaddptr))); ////GeoPos std::unique_ptr geoposptr = std::make_unique( - kCmdNameGeoPos, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo ); + kCmdNameGeoPos, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGeoPos, std::move(geoposptr))); ////GeoDist std::unique_ptr geodistptr = std::make_unique( - kCmdNameGeoDist, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo ); + kCmdNameGeoDist, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGeoDist, std::move(geodistptr))); ////GeoHash std::unique_ptr geohashptr = std::make_unique( - kCmdNameGeoHash, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo ); + kCmdNameGeoHash, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGeoHash, std::move(geohashptr))); ////GeoRadius std::unique_ptr georadiusptr = std::make_unique( - kCmdNameGeoRadius, -6, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsGeo ); + kCmdNameGeoRadius, -6, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameGeoRadius, std::move(georadiusptr))); ////GeoRadiusByMember std::unique_ptr georadiusbymemberptr = std::make_unique( - kCmdNameGeoRadiusByMember, -5, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsGeo ); + kCmdNameGeoRadiusByMember, -5, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsGeo | kCmdFlagsSlow); cmd_table->insert( std::pair>(kCmdNameGeoRadiusByMember, std::move(georadiusbymemberptr))); // PubSub ////Publish std::unique_ptr publishptr = - std::make_unique(kCmdNamePublish, 3, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNamePublish, 3, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNamePublish, std::move(publishptr))); ////Subscribe std::unique_ptr subscribeptr = - std::make_unique(kCmdNameSubscribe, -2, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNameSubscribe, -2, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameSubscribe, std::move(subscribeptr))); ////UnSubscribe std::unique_ptr unsubscribeptr = - std::make_unique(kCmdNameUnSubscribe, -1, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNameUnSubscribe, -1, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameUnSubscribe, std::move(unsubscribeptr))); ////PSubscribe std::unique_ptr psubscribeptr = - std::make_unique(kCmdNamePSubscribe, -2, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNamePSubscribe, -2, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePSubscribe, std::move(psubscribeptr))); ////PUnSubscribe std::unique_ptr punsubscribeptr = - std::make_unique(kCmdNamePUnSubscribe, -1, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNamePUnSubscribe, -1, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePUnSubscribe, std::move(punsubscribeptr))); ////PubSub std::unique_ptr pubsubptr = - std::make_unique(kCmdNamePubSub, -2, kCmdFlagsRead | kCmdFlagsPubSub ); + std::make_unique(kCmdNamePubSub, -2, kCmdFlagsRead | kCmdFlagsPubSub | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNamePubSub, std::move(pubsubptr))); + ////ACL + std::unique_ptr aclptr = std::make_unique(KCmdNameAcl, -2, kCmdFlagsAdmin | kCmdFlagsSlow); + cmd_table->insert(std::pair>(KCmdNameAcl, std::move(aclptr))); + // Transaction ////Multi std::unique_ptr multiptr = - std::make_unique(kCmdNameMulti, 1, kCmdFlagsRead | kCmdFlagsMultiSlot ); + std::make_unique(kCmdNameMulti, 1, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameMulti, std::move(multiptr))); ////Exec std::unique_ptr execptr = std::make_unique( - kCmdNameExec, 1, kCmdFlagsRead | kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSuspend ); + kCmdNameExec, 1, kCmdFlagsRead | kCmdFlagsWrite | kCmdFlagsMultiSlot | kCmdFlagsSuspend | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameExec, std::move(execptr))); ////Discard - std::unique_ptr discardptr = std::make_unique(kCmdNameDiscard, 1, kCmdFlagsRead ); + std::unique_ptr discardptr = std::make_unique(kCmdNameDiscard, 1, kCmdFlagsRead | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameDiscard, std::move(discardptr))); ////Watch - std::unique_ptr watchptr = std::make_unique(kCmdNameWatch, -2, kCmdFlagsRead ); + std::unique_ptr watchptr = std::make_unique(kCmdNameWatch, -2, kCmdFlagsRead | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameWatch, std::move(watchptr))); ////Unwatch - std::unique_ptr unwatchptr = std::make_unique(kCmdNameUnWatch, 1, kCmdFlagsRead ); + std::unique_ptr unwatchptr = std::make_unique(kCmdNameUnWatch, 1, kCmdFlagsRead | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameUnWatch, std::move(unwatchptr))); // Stream ////XAdd std::unique_ptr xaddptr = - std::make_unique(kCmdNameXAdd, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXAdd, -4, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameXAdd, std::move(xaddptr))); ////XLen std::unique_ptr xlenptr = - std::make_unique(kCmdNameXLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXLen, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameXLen, std::move(xlenptr))); ////XRead std::unique_ptr xreadptr = - std::make_unique(kCmdNameXRead, -3, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXRead, -3, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsStream | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameXRead, std::move(xreadptr))); ////XRange std::unique_ptr xrangeptr = - std::make_unique(kCmdNameXRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXRange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameXRange, std::move(xrangeptr))); ////XRerange std::unique_ptr xrerverangeptr = - std::make_unique(kCmdNameXRevrange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXRevrange, -4, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameXRevrange, std::move(xrerverangeptr))); ////XTrim std::unique_ptr xtrimptr = - std::make_unique(kCmdNameXTrim, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXTrim, -2, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameXTrim, std::move(xtrimptr))); ////XDel std::unique_ptr xdelptr = - std::make_unique(kCmdNameXDel, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXDel, -3, kCmdFlagsWrite | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsFast); cmd_table->insert(std::pair>(kCmdNameXDel, std::move(xdelptr))); ////XINFO std::unique_ptr xinfoptr = - std::make_unique(kCmdNameXInfo, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream); + std::make_unique(kCmdNameXInfo, -2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsStream | kCmdFlagsSlow); cmd_table->insert(std::pair>(kCmdNameXInfo, std::move(xinfoptr))); } @@ -829,6 +833,12 @@ Cmd* GetCmdFromDB(const std::string& opt, const CmdTable& cmd_table) { return nullptr; } +Cmd::Cmd(std::string name, int arity, uint32_t flag, uint32_t aclCategory) + : name_(std::move(name)), arity_(arity), flag_(flag), aclCategory_(aclCategory) { + // assign cmd id + cmdId_ = g_pika_cmd_table_manager->GetCmdId(); +} + void Cmd::Initial(const PikaCmdArgsType& argv, const std::string& db_name) { argv_ = argv; db_name_ = db_name; @@ -1007,17 +1017,32 @@ void Cmd::ProcessMultiSlotCmd() { } } +uint32_t Cmd::flag() const { return flag_; } bool Cmd::hasFlag(uint32_t flag) const { return (flag_ & flag); } bool Cmd::is_read() const { return (flag_ & kCmdFlagsRead); } bool Cmd::is_write() const { return (flag_ & kCmdFlagsWrite); } bool Cmd::IsLocal() const { return (flag_ & kCmdFlagsLocal); } +int8_t Cmd::SubCmdIndex(const std::string& cmdName) { + if (subCmdName_.empty()) { + return -1; + } + for (size_t i = 0; i < subCmdName_.size(); ++i) { + if (!strcasecmp(subCmdName_[i].data(), cmdName.data())) { + return i; + } + } + return -1; +} + // Others need to be suspended when a suspend command run bool Cmd::IsSuspend() const { return (flag_ & kCmdFlagsSuspend); } // Must with admin auth bool Cmd::is_single_slot() const { return (flag_ & kCmdFlagsSingleSlot); } bool Cmd::is_multi_slot() const { return (flag_ & kCmdFlagsMultiSlot); } // std::string Cmd::CurrentSubCommand() const { return ""; }; +bool Cmd::HasSubCommand() const { return subCmdName_.size() > 0; }; +std::vector Cmd::SubCommand() const { return subCmdName_; }; bool Cmd::IsAdminRequire() const { return (flag_ & kCmdFlagsAdminRequire); } bool Cmd::IsNeedUpdateCache() const { return (flag_ & kCmdFlagsUpdateCache); } bool Cmd::IsNeedCacheDo() const { @@ -1064,6 +1089,10 @@ std::string Cmd::db_name() const { return db_name_; } PikaCmdArgsType& Cmd::argv() { return argv_; } +uint32_t Cmd::AclCategory() const { return aclCategory_; } + +void Cmd::AddAclCategory(uint32_t aclCategory) { aclCategory_ |= aclCategory; } + std::string Cmd::ToRedisProtocol() { std::string content; content.reserve(RAW_ARGS_LEN); diff --git a/src/pika_conf.cc b/src/pika_conf.cc index 18e31c959b..181858fd3c 100644 --- a/src/pika_conf.cc +++ b/src/pika_conf.cc @@ -14,6 +14,7 @@ #include "pstd/include/pstd_string.h" #include "cache/include/config.h" +#include "include/acl.h" #include "include/pika_define.h" using pstd::Status; @@ -161,7 +162,7 @@ int PikaConf::Load() { GetConfStr("replication-id", &replication_id_); GetConfStr("requirepass", &requirepass_); GetConfStr("masterauth", &masterauth_); - GetConfStr("userpass", &userpass_); + // GetConfStr("userpass", &userpass_); GetConfInt("maxclients", &maxclients_); if (maxclients_ <= 0) { maxclients_ = 20000; @@ -196,12 +197,7 @@ int PikaConf::Load() { if (slowlog_max_len_ == 0) { slowlog_max_len_ = 128; } - std::string user_blacklist; - GetConfStr("userblacklist", &user_blacklist); - pstd::StringSplit(user_blacklist, COMMA, user_blacklist_); - for (auto& item : user_blacklist_) { - pstd::StringToLower(item); - } + GetConfInt("default-slot-num", &default_slot_num_); if (default_slot_num_ <= 0) { LOG(FATAL) << "config default-slot-num error," @@ -569,6 +565,24 @@ int PikaConf::Load() { network_interface_ = ""; GetConfStr("network-interface", &network_interface_); + // acl users + GetConfStrMulti("user", &users_); + + GetConfStr("aclfile", &aclFile_); + + std::string acl_pubsub_default; + GetConfStr("acl-pubsub-default", &acl_pubsub_default); + if (acl_pubsub_default == "allchannels") { + acl_pubsub_default_ = static_cast(AclSelectorFlag::ALL_CHANNELS); + } + + int tmp_acllog_max_len = 128; + GetConfInt("acllog-max-len", &tmp_acllog_max_len); + if (tmp_acllog_max_len < 0) { + tmp_acllog_max_len = 128; + } + acl_Log_max_len_ = tmp_acllog_max_len; + // slaveof slaveof_ = ""; GetConfStr("slaveof", &slaveof_); @@ -706,15 +720,15 @@ void PikaConf::SetCacheType(const std::string& value) { } int PikaConf::ConfigRewrite() { - std::string userblacklist = suser_blacklist(); + // std::string userblacklist = suser_blacklist(); std::lock_guard l(rwlock_); // Only set value for config item that can be config set. SetConfInt("timeout", timeout_); SetConfStr("requirepass", requirepass_); SetConfStr("masterauth", masterauth_); - SetConfStr("userpass", userpass_); - SetConfStr("userblacklist", userblacklist); + // SetConfStr("userpass", userpass_); + // SetConfStr("userblacklist", userblacklist); SetConfStr("dump-prefix", bgsave_prefix_); SetConfInt("maxclients", maxclients_); SetConfInt("dump-expire", expire_dump_days_); diff --git a/src/pika_dispatch_thread.cc b/src/pika_dispatch_thread.cc index 21842905db..ae94deaf7e 100644 --- a/src/pika_dispatch_thread.cc +++ b/src/pika_dispatch_thread.cc @@ -9,6 +9,7 @@ #include "include/pika_conf.h" #include "include/pika_server.h" +#include "net/src/dispatch_thread.h" #include "pstd/include/testutil.h" extern PikaServer* g_pika_server; @@ -44,6 +45,20 @@ bool PikaDispatchThread::ClientKill(const std::string& ip_port) { return thread_ void PikaDispatchThread::ClientKillAll() { thread_rep_->KillAllConns(); } +void PikaDispatchThread::UnAuthUserAndKillClient(const std::set& users, + const std::shared_ptr& defaultUser) { + auto dispatchThread = dynamic_cast(thread_rep_); + if (dispatchThread) { + dispatchThread->AllConn([&](const std::shared_ptr& conn) { + auto pikaClientConn = std::dynamic_pointer_cast(conn); + if (pikaClientConn && users.count(pikaClientConn->UserName())) { + pikaClientConn->UnAuth(defaultUser); + conn->SetClose(true); + } + }); + } +} + bool PikaDispatchThread::Handles::AccessHandle(std::string& ip) const { if (ip == "127.0.0.1") { ip = g_pika_server->host(); diff --git a/src/pika_pubsub.cc b/src/pika_pubsub.cc index 46e134ff4b..e3e2b567e4 100644 --- a/src/pika_pubsub.cc +++ b/src/pika_pubsub.cc @@ -48,6 +48,9 @@ void SubscribeCmd::DoInitial() { res_.SetRes(CmdRes::kWrongNum, kCmdNameSubscribe); return; } + for (size_t i = 1; i < argv_.size(); i++) { + channels_.push_back(argv_[i]); + } } void SubscribeCmd::Do(std::shared_ptr slot) { @@ -58,10 +61,6 @@ void SubscribeCmd::Do(std::shared_ptr slot) { return; } std::shared_ptr cli_conn = std::dynamic_pointer_cast(conn); - std::vector channels; - for (size_t i = 1; i < argv_.size(); i++) { - channels.push_back(argv_[i]); - } if (!cli_conn->IsPubSub()) { cli_conn->server_thread()->MoveConnOut(conn->fd()); cli_conn->SetIsPubSub(true); @@ -75,7 +74,7 @@ void SubscribeCmd::Do(std::shared_ptr slot) { }); } std::vector> result; - g_pika_server->Subscribe(conn, channels, name_ == kCmdNamePSubscribe, &result); + g_pika_server->Subscribe(conn, channels_, name_ == kCmdNamePSubscribe, &result); return res_.SetRes(CmdRes::kNone, ConstructPubSubResp(name_, result)); } @@ -84,14 +83,12 @@ void UnSubscribeCmd::DoInitial() { res_.SetRes(CmdRes::kWrongNum, kCmdNameUnSubscribe); return; } -} - -void UnSubscribeCmd::Do(std::shared_ptr slot) { - std::vector channels; for (size_t i = 1; i < argv_.size(); i++) { - channels.push_back(argv_[i]); + channels_.push_back(argv_[i]); } +} +void UnSubscribeCmd::Do(std::shared_ptr slot) { std::shared_ptr conn = GetConn(); if (!conn) { res_.SetRes(CmdRes::kErrOther, kCmdNameUnSubscribe); @@ -101,7 +98,7 @@ void UnSubscribeCmd::Do(std::shared_ptr slot) { std::shared_ptr cli_conn = std::dynamic_pointer_cast(conn); std::vector> result; - int subscribed = g_pika_server->UnSubscribe(conn, channels, name_ == kCmdNamePUnSubscribe, &result); + int subscribed = g_pika_server->UnSubscribe(conn, channels_, name_ == kCmdNamePUnSubscribe, &result); if (subscribed == 0 && cli_conn->IsPubSub()) { /* * if the number of client subscribed is zero, @@ -125,6 +122,9 @@ void PSubscribeCmd::DoInitial() { res_.SetRes(CmdRes::kWrongNum, kCmdNamePSubscribe); return; } + for (size_t i = 1; i < argv_.size(); i++) { + channels_.push_back(argv_[i]); + } } void PSubscribeCmd::Do(std::shared_ptr slot) { @@ -147,12 +147,8 @@ void PSubscribeCmd::Do(std::shared_ptr slot) { g_pika_server->EnablePublish(cli_conn->fd()); }); } - std::vector channels; - for (size_t i = 1; i < argv_.size(); i++) { - channels.push_back(argv_[i]); - } std::vector> result; - g_pika_server->Subscribe(conn, channels, name_ == kCmdNamePSubscribe, &result); + g_pika_server->Subscribe(conn, channels_, name_ == kCmdNamePSubscribe, &result); return res_.SetRes(CmdRes::kNone, ConstructPubSubResp(name_, result)); } @@ -161,14 +157,13 @@ void PUnSubscribeCmd::DoInitial() { res_.SetRes(CmdRes::kWrongNum, kCmdNamePUnSubscribe); return; } -} - -void PUnSubscribeCmd::Do(std::shared_ptr slot) { - std::vector channels; for (size_t i = 1; i < argv_.size(); i++) { - channels.push_back(argv_[i]); + channels_.push_back(argv_[i]); } +} + +void PUnSubscribeCmd::Do(std::shared_ptr slot) { std::shared_ptr conn = GetConn(); if (!conn) { res_.SetRes(CmdRes::kErrOther, kCmdNamePUnSubscribe); @@ -178,7 +173,7 @@ void PUnSubscribeCmd::Do(std::shared_ptr slot) { std::shared_ptr cli_conn = std::dynamic_pointer_cast(conn); std::vector> result; - int subscribed = g_pika_server->UnSubscribe(conn, channels, name_ == kCmdNamePUnSubscribe, &result); + int subscribed = g_pika_server->UnSubscribe(conn, channels_, name_ == kCmdNamePUnSubscribe, &result); if (subscribed == 0 && cli_conn->IsPubSub()) { /* * if the number of client subscribed is zero, diff --git a/src/pika_server.cc b/src/pika_server.cc index 5014635baf..0872d45979 100644 --- a/src/pika_server.cc +++ b/src/pika_server.cc @@ -17,17 +17,17 @@ #include "net/include/bg_thread.h" #include "net/include/net_cli.h" #include "net/include/net_interfaces.h" -#include "net/include/redis_cli.h" #include "net/include/net_stats.h" +#include "net/include/redis_cli.h" #include "pstd/include/env.h" #include "pstd/include/rsync.h" #include "include/pika_cmd_table_manager.h" #include "include/pika_dispatch_thread.h" -#include "include/pika_monotonic_time.h" #include "include/pika_instant.h" -#include "include/pika_server.h" +#include "include/pika_monotonic_time.h" #include "include/pika_rm.h" +#include "include/pika_server.h" using pstd::Status; extern PikaServer* g_pika_server; @@ -77,11 +77,12 @@ PikaServer::PikaServer() // We estimate the queue size int worker_queue_limit = g_pika_conf->maxclients() / worker_num_ + 100; LOG(INFO) << "Worker queue limit is " << worker_queue_limit; - for_each(ips.begin(), ips.end(), [](auto& ip) {LOG(WARNING) << ip;}); - pika_dispatch_thread_ = - std::make_unique(ips, port_, worker_num_, 3000, worker_queue_limit, g_pika_conf->max_conn_rbuf_size()); - pika_rsync_service_ = std::make_unique(g_pika_conf->db_sync_path(), g_pika_conf->port() + kPortShiftRSync); - //TODO: remove pika_rsync_service_,reuse pika_rsync_service_ port + for_each(ips.begin(), ips.end(), [](auto& ip) { LOG(WARNING) << ip; }); + pika_dispatch_thread_ = std::make_unique(ips, port_, worker_num_, 3000, worker_queue_limit, + g_pika_conf->max_conn_rbuf_size()); + pika_rsync_service_ = + std::make_unique(g_pika_conf->db_sync_path(), g_pika_conf->port() + kPortShiftRSync); + // TODO: remove pika_rsync_service_,reuse pika_rsync_service_ port rsync_server_ = std::make_unique(ips, port_ + kPortShiftRsync2); pika_pubsub_thread_ = std::make_unique(); pika_auxiliary_thread_ = std::make_unique(); @@ -93,7 +94,7 @@ PikaServer::PikaServer() exit_mutex_.lock(); int64_t lastsave = GetLastSaveTime(g_pika_conf->bgsave_path()); UpdateLastSave(lastsave); - + // init role std::string slaveof = g_pika_conf->slaveof(); if (!slaveof.empty()) { @@ -106,6 +107,8 @@ PikaServer::PikaServer() SetMaster(master_ip, master_port); } } + + acl_ = std::make_unique<::Acl>(); } PikaServer::~PikaServer() { @@ -248,7 +251,7 @@ void PikaServer::CheckLeaderProtectedMode() { } } -bool PikaServer::readonly(const std::string& db_name, const std::string& key) { +bool PikaServer::readonly(const std::string& db_name) { std::shared_lock l(state_protector_); return ((role_ & PIKA_ROLE_SLAVE) != 0) && g_pika_conf->slave_read_only(); } @@ -318,7 +321,7 @@ void PikaServer::InitDBStruct() { } } -Status PikaServer::AddDBStruct(const std::string &db_name, uint32_t num) { +Status PikaServer::AddDBStruct(const std::string& db_name, uint32_t num) { std::shared_ptr db = g_pika_server->GetDB(db_name); if (db) { return Status::Corruption("db already exist"); @@ -331,7 +334,7 @@ Status PikaServer::AddDBStruct(const std::string &db_name, uint32_t num) { return Status::OK(); } -Status PikaServer::DelDBStruct(const std::string &db_name) { +Status PikaServer::DelDBStruct(const std::string& db_name) { std::shared_ptr db = g_pika_server->GetDB(db_name); if (!db) { return Status::Corruption("db not found"); @@ -485,10 +488,9 @@ void PikaServer::PrepareSlotTrySync() { ReplState state = force_full_sync_ ? ReplState::kTryDBSync : ReplState::kTryConnect; for (const auto& db_item : dbs_) { for (const auto& slot_item : db_item.second->slots_) { - Status s = g_pika_rm->ActivateSyncSlaveSlot( - RmNode(g_pika_server->master_ip(), g_pika_server->master_port(), db_item.second->GetDBName(), - slot_item.second->GetSlotID()), - state); + Status s = g_pika_rm->ActivateSyncSlaveSlot(RmNode(g_pika_server->master_ip(), g_pika_server->master_port(), + db_item.second->GetDBName(), slot_item.second->GetSlotID()), + state); if (!s.ok()) { LOG(WARNING) << s.ToString(); } @@ -565,21 +567,21 @@ Status PikaServer::DoSameThingEverySlot(const TaskType& type) { for (const auto& slot_item : db_item.second->slots_) { switch (type) { case TaskType::kResetReplState: { - slave_slot = g_pika_rm->GetSyncSlaveSlotByName( - SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); + slave_slot = + g_pika_rm->GetSyncSlaveSlotByName(SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); if (!slave_slot) { - LOG(WARNING) << "Slave Slot: " << db_item.second->GetDBName() << ":" - << slot_item.second->GetSlotID() << " Not Found"; + LOG(WARNING) << "Slave Slot: " << db_item.second->GetDBName() << ":" << slot_item.second->GetSlotID() + << " Not Found"; } slave_slot->SetReplState(ReplState::kNoConnect); break; } case TaskType::kPurgeLog: { - std::shared_ptr slot = g_pika_rm->GetSyncMasterSlotByName( - SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); + std::shared_ptr slot = + g_pika_rm->GetSyncMasterSlotByName(SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); if (!slot) { - LOG(WARNING) << "Slot: " << db_item.second->GetDBName() << ":" - << slot_item.second->GetSlotID() << " Not Found."; + LOG(WARNING) << "Slot: " << db_item.second->GetDBName() << ":" << slot_item.second->GetSlotID() + << " Not Found."; break; } slot->StableLogger()->PurgeStableLogs(); @@ -671,8 +673,7 @@ int32_t PikaServer::GetSlaveListString(std::string& slave_list_str) { << ",lag="; for (const auto& ts : slave.db_structs) { for (size_t idx = 0; idx < ts.slot_num; ++idx) { - std::shared_ptr slot = - g_pika_rm->GetSyncMasterSlotByName(SlotInfo(ts.db_name, idx)); + std::shared_ptr slot = g_pika_rm->GetSyncMasterSlotByName(SlotInfo(ts.db_name, idx)); if (!slot) { LOG(WARNING) << "Sync Master Slot: " << ts.db_name << ":" << idx << ", NotFound"; continue; @@ -684,9 +685,9 @@ int32_t PikaServer::GetSlaveListString(std::string& slave_list_str) { if (!s.ok()) { continue; } else { - uint64_t lag = - static_cast((master_boffset.filenum - sent_slave_boffset.filenum)) * g_pika_conf->binlog_file_size() + - master_boffset.offset - sent_slave_boffset.offset; + uint64_t lag = static_cast((master_boffset.filenum - sent_slave_boffset.filenum)) * + g_pika_conf->binlog_file_size() + + master_boffset.offset - sent_slave_boffset.offset; tmp_stream << "(" << slot->SlotName() << ":" << lag << ")"; } } else { @@ -702,8 +703,7 @@ int32_t PikaServer::GetSlaveListString(std::string& slave_list_str) { // Try add Slave, return true if success, // return false when slave already exist -bool PikaServer::TryAddSlave(const std::string& ip, int64_t port, int fd, - const std::vector& db_structs) { +bool PikaServer::TryAddSlave(const std::string& ip, int64_t port, int fd, const std::vector& db_structs) { std::string ip_port = pstd::IpPortString(ip, static_cast(port)); std::lock_guard l(slave_mutex_); @@ -804,11 +804,11 @@ bool PikaServer::AllSlotConnectSuccess() { std::shared_ptr slave_slot = nullptr; for (const auto& db_item : dbs_) { for (const auto& slot_item : db_item.second->slots_) { - slave_slot = g_pika_rm->GetSyncSlaveSlotByName( - SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); + slave_slot = + g_pika_rm->GetSyncSlaveSlotByName(SlotInfo(db_item.second->GetDBName(), slot_item.second->GetSlotID())); if (!slave_slot) { - LOG(WARNING) << "Slave Slot: " << db_item.second->GetDBName() << ":" - << slot_item.second->GetSlotID() << ", NotFound"; + LOG(WARNING) << "Slave Slot: " << db_item.second->GetDBName() << ":" << slot_item.second->GetSlotID() + << ", NotFound"; return false; } @@ -923,7 +923,8 @@ pstd::Status PikaServer::GetDumpUUID(const std::string& db_name, const uint32_t return pstd::Status::OK(); } -pstd::Status PikaServer::GetDumpMeta(const std::string& db_name, const uint32_t slot_id, std::vector* fileNames, std::string* snapshot_uuid) { +pstd::Status PikaServer::GetDumpMeta(const std::string& db_name, const uint32_t slot_id, + std::vector* fileNames, std::string* snapshot_uuid) { std::shared_ptr slot = GetDBSlotById(db_name, slot_id); if (!slot) { LOG(WARNING) << "cannot find slot for db_name " << db_name << " slot_id: " << slot_id; @@ -933,19 +934,15 @@ pstd::Status PikaServer::GetDumpMeta(const std::string& db_name, const uint32_t return pstd::Status::OK(); } -void PikaServer::TryDBSync(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id, - int32_t top) { +void PikaServer::TryDBSync(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id, int32_t top) { std::shared_ptr slot = GetDBSlotById(db_name, slot_id); if (!slot) { - LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name - << ", TryDBSync Failed"; + LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name << ", TryDBSync Failed"; return; } - std::shared_ptr sync_slot = - g_pika_rm->GetSyncMasterSlotByName(SlotInfo(db_name, slot_id)); + std::shared_ptr sync_slot = g_pika_rm->GetSyncMasterSlotByName(SlotInfo(db_name, slot_id)); if (!sync_slot) { - LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name - << ", TryDBSync Failed"; + LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name << ", TryDBSync Failed"; return; } BgSaveInfo bgsave_info = slot->bgsave_info(); @@ -956,15 +953,14 @@ void PikaServer::TryDBSync(const std::string& ip, int port, const std::string& d // Need Bgsave first slot->BgSaveSlot(); } - //TODO: temporarily disable rsync server - //DBSync(ip, port, db_name, slot_id); + // TODO: temporarily disable rsync server + // DBSync(ip, port, db_name, slot_id); } void PikaServer::DbSyncSendFile(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id) { std::shared_ptr slot = GetDBSlotById(db_name, slot_id); if (!slot) { - LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name - << ", DbSync send file Failed"; + LOG(WARNING) << "can not find Slot whose id is " << slot_id << " in db " << db_name << ", DbSync send file Failed"; return; } @@ -1074,8 +1070,7 @@ void PikaServer::DbSyncSendFile(const std::string& ip, int port, const std::stri } } -std::string PikaServer::DbSyncTaskIndex(const std::string& ip, int port, const std::string& db_name, - uint32_t slot_id) { +std::string PikaServer::DbSyncTaskIndex(const std::string& ip, int port, const std::string& db_name, uint32_t slot_id) { char buf[256]; snprintf(buf, sizeof(buf), "%s:%d_%s:%d", ip.data(), port, db_name.data(), slot_id); return buf; @@ -1105,6 +1100,10 @@ bool PikaServer::HasMonitorClients() const { std::unique_lock lock(monitor_mutex_protector_); return !pika_monitor_clients_.empty(); } +bool PikaServer::ClientIsMonitor(const std::shared_ptr& client_ptr) const { + std::unique_lock lock(monitor_mutex_protector_); + return pika_monitor_clients_.count(client_ptr) != 0; +} void PikaServer::AddMonitorMessage(const std::string& monitor_message) { const std::string msg = "+" + monitor_message + "\r\n"; @@ -1126,7 +1125,7 @@ void PikaServer::AddMonitorMessage(const std::string& monitor_message) { cli->WriteResp(msg); cli->SendReply(); } - lock.unlock(); // SendReply without lock + lock.unlock(); // SendReply without lock } void PikaServer::AddMonitorClient(const std::shared_ptr& client_ptr) { @@ -1222,29 +1221,20 @@ void PikaServer::ResetLastSecQuerynum() { statistic_.ResetDBLastSecQuerynum(); } -void PikaServer::UpdateQueryNumAndExecCountDB(const std::string& db_name, const std::string& command, - bool is_write) { +void PikaServer::UpdateQueryNumAndExecCountDB(const std::string& db_name, const std::string& command, bool is_write) { std::string cmd(command); statistic_.server_stat.qps.querynum++; statistic_.server_stat.exec_count_db[pstd::StringToUpper(cmd)]++; statistic_.UpdateDBQps(db_name, command, is_write); } -size_t PikaServer::NetInputBytes() { - return g_network_statistic->NetInputBytes(); -} +size_t PikaServer::NetInputBytes() { return g_network_statistic->NetInputBytes(); } -size_t PikaServer::NetOutputBytes() { - return g_network_statistic->NetOutputBytes(); -} +size_t PikaServer::NetOutputBytes() { return g_network_statistic->NetOutputBytes(); } -size_t PikaServer::NetReplInputBytes() { - return g_network_statistic->NetReplInputBytes(); -} +size_t PikaServer::NetReplInputBytes() { return g_network_statistic->NetReplInputBytes(); } -size_t PikaServer::NetReplOutputBytes() { - return g_network_statistic->NetReplOutputBytes(); -} +size_t PikaServer::NetReplOutputBytes() { return g_network_statistic->NetReplOutputBytes(); } float PikaServer::InstantaneousInputKbps() { return static_cast(g_pika_server->instant_->getInstantaneousMetric(STATS_METRIC_NET_INPUT)) / 1024.0f; @@ -1255,11 +1245,13 @@ float PikaServer::InstantaneousOutputKbps() { } float PikaServer::InstantaneousInputReplKbps() { - return static_cast(g_pika_server->instant_->getInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION)) / 1024.0f; + return static_cast(g_pika_server->instant_->getInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION)) / + 1024.0f; } float PikaServer::InstantaneousOutputReplKbps() { - return static_cast(g_pika_server->instant_->getInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION)) / 1024.0f; + return static_cast(g_pika_server->instant_->getInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION)) / + 1024.0f; } std::unordered_map PikaServer::ServerExecCountDB() { @@ -1291,14 +1283,14 @@ void PikaServer::EnablePublish(int fd) { pika_pubsub_thread_->UpdateConnReadyState(fd, net::PubSubThread::ReadyState::kReady); } -int PikaServer::UnSubscribe(const std::shared_ptr &conn, const std::vector& channels, bool pattern, - std::vector>* result) { +int PikaServer::UnSubscribe(const std::shared_ptr& conn, const std::vector& channels, + bool pattern, std::vector>* result) { int subscribed = pika_pubsub_thread_->UnSubscribe(conn, channels, pattern, result); return subscribed; } -void PikaServer::Subscribe(const std::shared_ptr &conn, const std::vector& channels, bool pattern, - std::vector>* result) { +void PikaServer::Subscribe(const std::shared_ptr& conn, const std::vector& channels, + bool pattern, std::vector>* result) { pika_pubsub_thread_->Subscribe(conn, channels, pattern, result); } @@ -1311,6 +1303,14 @@ void PikaServer::PubSubNumSub(const std::vector& channels, pika_pubsub_thread_->PubSubNumSub(channels, result); } +int PikaServer::ClientPubSubChannelSize(const std::shared_ptr& conn) { + return pika_pubsub_thread_->ClientPubSubChannelSize(conn); +} + +int PikaServer::ClientPubSubChannelPatternSize(const std::shared_ptr& conn) { + return pika_pubsub_thread_->ClientPubSubChannelPatternSize(conn); +} + /******************************* PRIVATE *******************************/ void PikaServer::DoTimingTask() { @@ -1321,8 +1321,8 @@ void PikaServer::DoTimingTask() { // Delete expired dump AutoDeleteExpiredDump(); // Cheek Rsync Status - //TODO: temporarily disable rsync - //AutoKeepAliveRSync(); + // TODO: temporarily disable rsync + // AutoKeepAliveRSync(); // Reset server qps ResetLastSecQuerynum(); // Auto update network instantaneous metric @@ -1448,9 +1448,8 @@ void PikaServer::AutoDeleteExpiredDump() { return; } // Handle dump directory - for (auto & i : dump_dir) { - if (i.substr(0, db_sync_prefix.size()) != db_sync_prefix || - i.size() != (db_sync_prefix.size() + 8)) { + for (auto& i : dump_dir) { + if (i.substr(0, db_sync_prefix.size()) != db_sync_prefix || i.size() != (db_sync_prefix.size() + 8)) { continue; } @@ -1515,13 +1514,15 @@ void PikaServer::AutoKeepAliveRSync() { void PikaServer::AutoUpdateNetworkMetric() { monotime current_time = getMonotonicUs(); - size_t factor = 5e6; // us, 5s - instant_->trackInstantaneousMetric(STATS_METRIC_NET_INPUT, g_pika_server->NetInputBytes() + g_pika_server->NetReplInputBytes(), + size_t factor = 5e6; // us, 5s + instant_->trackInstantaneousMetric(STATS_METRIC_NET_INPUT, + g_pika_server->NetInputBytes() + g_pika_server->NetReplInputBytes(), current_time, + factor); + instant_->trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT, + g_pika_server->NetOutputBytes() + g_pika_server->NetReplOutputBytes(), current_time, factor); - instant_->trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT, g_pika_server->NetOutputBytes() + g_pika_server->NetReplOutputBytes(), + instant_->trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION, g_pika_server->NetReplInputBytes(), current_time, factor); - instant_->trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION, g_pika_server->NetReplInputBytes(), current_time, - factor); instant_->trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION, g_pika_server->NetReplOutputBytes(), current_time, factor); } @@ -1655,15 +1656,17 @@ void PikaServer::ServerStatus(std::string* info) { info->append(tmp_stream.str()); } -bool PikaServer:: SlotsMigrateBatch(const std::string &ip, int64_t port, int64_t time_out, int64_t slot_num,int64_t keys_num, const std::shared_ptr& slot) { +bool PikaServer::SlotsMigrateBatch(const std::string& ip, int64_t port, int64_t time_out, int64_t slot_num, + int64_t keys_num, const std::shared_ptr& slot) { return pika_migrate_thread_->ReqMigrateBatch(ip, port, time_out, slot_num, keys_num, slot); } -void PikaServer:: GetSlotsMgrtSenderStatus(std::string *ip, int64_t *port, int64_t *slot, bool *migrating, int64_t *moved, int64_t *remained){ +void PikaServer::GetSlotsMgrtSenderStatus(std::string* ip, int64_t* port, int64_t* slot, bool* migrating, + int64_t* moved, int64_t* remained) { return pika_migrate_thread_->GetMigrateStatus(ip, port, slot, migrating, moved, remained); } -int PikaServer:: SlotsMigrateOne(const std::string &key, const std::shared_ptr& slot) { +int PikaServer::SlotsMigrateOne(const std::string& key, const std::shared_ptr& slot) { return pika_migrate_thread_->ReqMigrateOne(key, slot); } @@ -1698,6 +1701,8 @@ void PikaServer::Bgslotsreload(const std::shared_ptr& slot) { bgsave_thread_.Schedule(&DoBgslotsreload, static_cast(this)); } +std::unordered_map* PikaServer::GetCommandStatMap() { return &cmdstat_map_; } + void DoBgslotsreload(void* arg) { auto p = static_cast(arg); PikaServer::BGSlotsReload reload = p->bgslots_reload(); @@ -1706,17 +1711,18 @@ void DoBgslotsreload(void* arg) { rocksdb::Status s; std::vector keys; int64_t cursor_ret = -1; - while(cursor_ret != 0 && p->GetSlotsreloading()){ + while (cursor_ret != 0 && p->GetSlotsreloading()) { cursor_ret = reload.slot->db()->Scan(storage::DataType::kAll, reload.cursor, reload.pattern, reload.count, &keys); std::vector::const_iterator iter; - for (iter = keys.begin(); iter != keys.end(); iter++){ + for (iter = keys.begin(); iter != keys.end(); iter++) { std::string key_type; int s = GetKeyType(*iter, key_type, reload.slot); - //if key is slotkey, can't add to SlotKey - if (s > 0){ - if (key_type == "s" && ((*iter).find(SlotKeyPrefix) != std::string::npos || (*iter).find(SlotTagPrefix) != std::string::npos)){ + // if key is slotkey, can't add to SlotKey + if (s > 0) { + if (key_type == "s" && + ((*iter).find(SlotKeyPrefix) != std::string::npos || (*iter).find(SlotTagPrefix) != std::string::npos)) { continue; } @@ -1732,7 +1738,7 @@ void DoBgslotsreload(void* arg) { if (cursor_ret == 0) { LOG(INFO) << "Finish slot reloading"; - } else{ + } else { LOG(INFO) << "Stop slot reloading"; } } @@ -1783,6 +1789,20 @@ int64_t PikaServer::GetLastSaveTime(const std::string& dir_path) { return 0; } +void PikaServer::AllClientUnAuth(const std::set& users) { + pika_dispatch_thread_->UnAuthUserAndKillClient(users, acl_->GetUserLock(Acl::DefaultUser)); +} + +void PikaServer::CheckPubsubClientKill(const std::string& userName, const std::vector& allChannel) { + pika_pubsub_thread_->ConnCanSubscribe(allChannel, [&](const std::shared_ptr& conn) -> bool { + auto pikaConn = std::dynamic_pointer_cast(conn); + if (pikaConn && pikaConn->UserName() == userName) { + return true; + } + return false; + }); +} + void DoBgslotscleanup(void* arg) { auto p = static_cast(arg); PikaServer::BGSlotsCleanup cleanup = p->bgslots_cleanup(); @@ -1791,23 +1811,24 @@ void DoBgslotscleanup(void* arg) { std::vector keys; int64_t cursor_ret = -1; std::vector cleanupSlots(cleanup.cleanup_slots); - while (cursor_ret != 0 && p->GetSlotscleaningup()){ - cursor_ret = g_pika_server->bgslots_cleanup_.slot->db()->Scan(storage::DataType::kAll, cleanup.cursor, cleanup.pattern, cleanup.count, &keys); + while (cursor_ret != 0 && p->GetSlotscleaningup()) { + cursor_ret = g_pika_server->bgslots_cleanup_.slot->db()->Scan(storage::DataType::kAll, cleanup.cursor, + cleanup.pattern, cleanup.count, &keys); std::string key_type; std::vector::const_iterator iter; - for (iter = keys.begin(); iter != keys.end(); iter++){ - if ((*iter).find(SlotKeyPrefix) != std::string::npos || (*iter).find(SlotTagPrefix) != std::string::npos){ + for (iter = keys.begin(); iter != keys.end(); iter++) { + if ((*iter).find(SlotKeyPrefix) != std::string::npos || (*iter).find(SlotTagPrefix) != std::string::npos) { continue; } - if (std::find(cleanupSlots.begin(), cleanupSlots.end(), GetSlotID(*iter)) != cleanupSlots.end()){ + if (std::find(cleanupSlots.begin(), cleanupSlots.end(), GetSlotID(*iter)) != cleanupSlots.end()) { if (GetKeyType(*iter, key_type, g_pika_server->bgslots_cleanup_.slot) <= 0) { LOG(WARNING) << "slots clean get key type for slot " << GetSlotID(*iter) << " key " << *iter << " error"; - continue ; + continue; } - if (DeleteKey(*iter, key_type[0], g_pika_server->bgslots_cleanup_.slot) <= 0){ - LOG(WARNING) << "slots clean del for slot " << GetSlotID(*iter) << " key "<< *iter << " error"; + if (DeleteKey(*iter, key_type[0], g_pika_server->bgslots_cleanup_.slot) <= 0) { + LOG(WARNING) << "slots clean del for slot " << GetSlotID(*iter) << " key " << *iter << " error"; } } } @@ -1817,7 +1838,7 @@ void DoBgslotscleanup(void* arg) { keys.clear(); } - for (int cleanupSlot : cleanupSlots){ + for (int cleanupSlot : cleanupSlots) { WriteDelKeyToBinlog(GetSlotKey(cleanupSlot), g_pika_server->bgslots_cleanup_.slot); WriteDelKeyToBinlog(GetSlotsTagKey(cleanupSlot), g_pika_server->bgslots_cleanup_.slot); } diff --git a/src/pika_slot_command.cc b/src/pika_slot_command.cc index 7279f7b20f..1c7997a418 100644 --- a/src/pika_slot_command.cc +++ b/src/pika_slot_command.cc @@ -86,35 +86,6 @@ net::NetCli *PikaMigrate::GetMigrateClient(const std::string &host, const int po LOG(INFO) << "GetMigrateClient: new migrate_cli[" << ip_port.c_str() << "]"; - std::string userpass = g_pika_conf->userpass(); - if (userpass != "") { - net::RedisCmdArgsType argv; - std::string wbuf_str; - argv.emplace_back("auth"); - argv.emplace_back(userpass); - net::SerializeRedisCommand(argv, &wbuf_str); - - s = migrate_cli->Send(&wbuf_str); - if (!s.ok()) { - LOG(ERROR) << "GetMigrateClient: new migrate_cli Send, error: " << s.ToString(); - delete migrate_cli; - return nullptr; - } - - s = migrate_cli->Recv(&argv); - if (!s.ok()) { - LOG(ERROR) << "GetMigrateClient: new migrate_cli Recv, error: " << s.ToString(); - delete migrate_cli; - return nullptr; - } - - if (strcasecmp(argv[0].data(), kInnerReplOk.data()) != 0) { - LOG(ERROR) << "GetMigrateClient: new migrate_cli auth error"; - delete migrate_cli; - return nullptr; - } - } - // add a new migrate client to the map migrate_clients_[ip_port] = migrate_cli; } else { diff --git a/src/pstd/examples/hash_example.cc b/src/pstd/examples/hash_example.cc index 9070d65a1c..114a99c63e 100644 --- a/src/pstd/examples/hash_example.cc +++ b/src/pstd/examples/hash_example.cc @@ -12,5 +12,9 @@ int main() { std::cout << "sha256('" << input << "'): " << output1 << std::endl; std::cout << "md5('" << input << "'): " << output2 << std::endl; + std::cout << "input is Sha256 " << isSha256(input) << std::endl; + + std::cout << "output1 is Sha256 " << isSha256(output1) << std::endl; + return 0; } diff --git a/src/pstd/include/base_conf.h b/src/pstd/include/base_conf.h index 779ab48bdf..d89d27fb31 100644 --- a/src/pstd/include/base_conf.h +++ b/src/pstd/include/base_conf.h @@ -31,11 +31,11 @@ class BaseConf { ConfType type; // 0 means conf, 1 means comment std::string name; std::string value; - ConfItem(ConfType t, std::string v) : type(t), value(std::move(v)) {} - ConfItem(ConfType t, std::string n, std::string v) : type(t), name(std::move(n)), value(std::move(v)) {} + ConfItem(ConfType t, std::string v) : type(t), value(std::move(v)) {} + ConfItem(ConfType t, std::string n, std::string v) : type(t), name(std::move(n)), value(std::move(v)) {} }; - explicit Rep(std::string p) : path(std::move(p)) {} + explicit Rep(std::string p) : path(std::move(p)) {} std::vector item; }; @@ -55,6 +55,7 @@ class BaseConf { bool GetConfBool(const std::string& name, bool* value) const; bool GetConfStrVec(const std::string& name, std::vector* value) const; bool GetConfDouble(const std::string& name, double* value) const; + bool GetConfStrMulti(const std::string& name, std::vector* values) const; bool SetConfInt(const std::string& name, int value); bool SetConfInt64(const std::string& name, int64_t value); diff --git a/src/pstd/include/pstd_hash.h b/src/pstd/include/pstd_hash.h index 54c12bc332..deb8924160 100644 --- a/src/pstd/include/pstd_hash.h +++ b/src/pstd/include/pstd_hash.h @@ -83,6 +83,8 @@ namespace pstd { std::string md5(const std::string& str, bool raw = false); std::string sha256(const std::string& input, bool raw = false); +bool isSha256(const std::string& input); + // a small class for calculating MD5 hashes of strings or byte arrays // it is not meant to be fast or secure // diff --git a/src/pstd/include/pstd_status.h b/src/pstd/include/pstd_status.h index c7c44f6dca..e73282657f 100644 --- a/src/pstd/include/pstd_status.h +++ b/src/pstd/include/pstd_status.h @@ -23,9 +23,7 @@ class Status { static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kNotFound, msg, msg2); } static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kCorruption, msg, msg2); } static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kNotSupported, msg, msg2); } - static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { - return {kInvalidArgument, msg, msg2}; - } + static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { return {kInvalidArgument, msg, msg2}; } static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kIOError, msg, msg2); } static Status EndFile(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kEndFile, msg, msg2); } @@ -41,6 +39,8 @@ class Status { static Status ItemNotExist(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kItemNotExist, msg, msg2); } + static Status Error(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kError, msg, msg2); } + // Returns true if the status indicates success. bool ok() const { return !state_; } @@ -77,6 +77,8 @@ class Status { // Return true if the status is Busy bool IsBusy() const { return code() == kBusy; } + bool IsError() const { return code() == kError; } + // Return a string representation of this status suitable for printing. // Returns the string "OK" for success. std::string ToString() const; @@ -102,7 +104,8 @@ class Status { kTimeout = 9, kAuthFailed = 10, kBusy = 11, - kItemNotExist = 12 + kItemNotExist = 12, + kError = 13 }; Code code() const { return !state_ ? kOk : static_cast(state_[4]); } diff --git a/src/pstd/include/pstd_string.h b/src/pstd/include/pstd_string.h index 84de1ddf4a..3606a5552f 100644 --- a/src/pstd/include/pstd_string.h +++ b/src/pstd/include/pstd_string.h @@ -59,6 +59,8 @@ bool ParseIpPortString(const std::string& ip_port, std::string& ip, int& port); std::string StringTrim(const std::string& ori, const std::string& charlist = " "); std::string getRandomHexChars(size_t len); +bool isspace(const std::string& str); + } // namespace pstd #endif // __PSTD_STRING_H__ diff --git a/src/pstd/src/base_conf.cc b/src/pstd/src/base_conf.cc index 19e8de1df4..e73878d702 100644 --- a/src/pstd/src/base_conf.cc +++ b/src/pstd/src/base_conf.cc @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include "pstd/include/env.h" #include "pstd/include/pstd_string.h" @@ -52,10 +52,13 @@ int BaseConf::LoadConf() { break; } switch (line[i]) { - case SPACE: case '\r': case '\n': break; + case SPACE: + if (value_len == 0) { // Allow spaces in value + break; + } case COLON: if (sep_sign == 0) { type = Rep::kConf; @@ -93,7 +96,7 @@ int BaseConf::ReloadConf() { } bool BaseConf::GetConfInt(const std::string& name, int* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -106,7 +109,7 @@ bool BaseConf::GetConfInt(const std::string& name, int* value) const { } bool BaseConf::GetConfIntHuman(const std::string& name, int* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -128,7 +131,7 @@ bool BaseConf::GetConfIntHuman(const std::string& name, int* value) const { } bool BaseConf::GetConfInt64Human(const std::string& name, int64_t* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -150,7 +153,7 @@ bool BaseConf::GetConfInt64Human(const std::string& name, int64_t* value) const } bool BaseConf::GetConfInt64(const std::string& name, int64_t* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -163,7 +166,7 @@ bool BaseConf::GetConfInt64(const std::string& name, int64_t* value) const { } bool BaseConf::GetConfStr(const std::string& name, std::string* val) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == 1) { continue; } @@ -176,7 +179,7 @@ bool BaseConf::GetConfStr(const std::string& name, std::string* val) const { } bool BaseConf::GetConfStrVec(const std::string& name, std::vector* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -199,7 +202,7 @@ bool BaseConf::GetConfStrVec(const std::string& name, std::vector* } bool BaseConf::GetConfBool(const std::string& name, bool* value) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -228,8 +231,20 @@ bool BaseConf::GetConfDouble(const std::string& name, double* value) const { return false; } +bool BaseConf::GetConfStrMulti(const std::string& name, std::vector* values) const { + for (auto& i : rep_->item) { + if (i.type == Rep::kComment) { + continue; + } + if (name == i.name) { + values->emplace_back(i.value); + } + } + return true; +} + bool BaseConf::SetConfInt(const std::string& name, const int value) { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -242,7 +257,7 @@ bool BaseConf::SetConfInt(const std::string& name, const int value) { } bool BaseConf::SetConfInt64(const std::string& name, const int64_t value) { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -255,7 +270,7 @@ bool BaseConf::SetConfInt64(const std::string& name, const int64_t value) { } bool BaseConf::SetConfStr(const std::string& name, const std::string& value) { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -268,7 +283,7 @@ bool BaseConf::SetConfStr(const std::string& name, const std::string& value) { } bool BaseConf::SetConfBool(const std::string& name, const bool value) { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -290,7 +305,7 @@ bool BaseConf::SetConfStrVec(const std::string& name, const std::vectoritem) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -303,7 +318,7 @@ bool BaseConf::SetConfDouble(const std::string& name, const double value) { } bool BaseConf::CheckConfExist(const std::string& name) const { - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kComment) { continue; } @@ -316,7 +331,7 @@ bool BaseConf::CheckConfExist(const std::string& name) const { void BaseConf::DumpConf() const { int cnt = 1; - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kConf) { LOG(INFO) << fmt::format("{:2} {} {}", cnt++, i.name, i.value); } @@ -332,7 +347,7 @@ bool BaseConf::WriteBack() { return false; } std::string tmp; - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kConf) { tmp = i.name + " : " + i.value + "\n"; write_file->Append(tmp); @@ -351,7 +366,7 @@ void BaseConf::WriteSampleConf() const { std::string sample_path = rep_->path + ".sample"; Status ret = NewWritableFile(sample_path, write_file); std::string tmp; - for (auto & i : rep_->item) { + for (auto& i : rep_->item) { if (i.type == Rep::kConf) { tmp = i.name + " :\n"; write_file->Append(tmp); diff --git a/src/pstd/src/pstd_hash.cc b/src/pstd/src/pstd_hash.cc index af9deed397..9fb4cba77d 100644 --- a/src/pstd/src/pstd_hash.cc +++ b/src/pstd/src/pstd_hash.cc @@ -249,6 +249,17 @@ std::string sha256(const std::string& input, bool raw) { return {buf}; } +bool isSha256(const std::string& input) { + if (input.size() != SHA256::DIGEST_SIZE * 2) { + return false; + } + for (const auto& item : input) { + if ((item < 'a' || item > 'f') && (item < '0' || item > '9')) { + return false; + } + } + return true; +} // MD5 hash function // Constants for MD5Transform routine. diff --git a/src/pstd/src/pstd_status.cc b/src/pstd/src/pstd_status.cc index fd62b79ebd..7cfd37d6ee 100644 --- a/src/pstd/src/pstd_status.cc +++ b/src/pstd/src/pstd_status.cc @@ -76,6 +76,9 @@ std::string Status::ToString() const { case kBusy: type = "Busy:"; break; + case kError: + type = ""; + break; default: snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", static_cast(code())); type = tmp; diff --git a/src/pstd/src/pstd_string.cc b/src/pstd/src/pstd_string.cc index 736a1b7604..97ec01e435 100644 --- a/src/pstd/src/pstd_string.cc +++ b/src/pstd/src/pstd_string.cc @@ -732,4 +732,8 @@ std::string StringTrim(const std::string& ori, const std::string& charlist) { return ori.substr(pos, rpos - pos + 1); } +bool isspace(const std::string& str) { + return std::count_if(str.begin(), str.end(), [](unsigned char c) { return std::isspace(c); }); +} + } // namespace pstd diff --git a/tests/assets/default.conf b/tests/assets/default.conf index 68936b1660..7c0c0f791d 100644 --- a/tests/assets/default.conf +++ b/tests/assets/default.conf @@ -1,81 +1,499 @@ -# Pika port +########################### +# Pika configuration file # +########################### + +# Pika port, the default value is 9221. +# [NOTICE] Port Magic offsets of port+1000 / port+2000 are used by Pika at present. +# Port 10221 is used for Rsync, and port 11221 is used for Replication, while the listening port is 9221. port : 9221 -# Thread Number + +# Random value identifying the Pika server, its string length must be 40. +# If not set, Pika will generate a random string with a length of 40 random characters. +# run-id : + +# Master's run-id +# master-run-id : + +# The number of threads for running Pika. +# It's not recommended to set this value exceeds +# the number of CPU cores on the deployment server. thread-num : 1 -# Sync Thread Number + +# Size of the thread pool, The threads within this pool +# are dedicated to handling user requests. +thread-pool-size : 12 + +# The number of sync-thread for data replication from master, those are the threads work on slave nodes +# and are used to execute commands sent from master node when replicating. sync-thread-num : 6 -# Item count of sync thread queue -sync-buffer-size : 10 -# Pika log path + +# Directory to store log files of Pika, which contains multiple types of logs, +# Including: INFO, WARNING, ERROR log, as well as binglog(write2fine) file which +# is used for replication. log-path : ./log/ -# Pika glog level: only INFO and ERROR -loglevel : info -# Pika db path + +# Directory to store the data of Pika. db-path : ./db/ -# Pika write-buffer-size -write-buffer-size : 268435456 -# Pika timeout + +# The size of a single RocksDB memtable at the Pika's bottom layer(Pika use RocksDB to store persist data). +# [Tip] Big write-buffer-size can improve writing performance, +# but this will generate heavier IO load when flushing from buffer to disk, +# you should configure it based on you usage scenario. +# Supported Units [K|M|G], write-buffer-size default unit is in [bytes]. +write-buffer-size : 256M + +# The size of one block in arena memory allocation. +# If <= 0, a proper value is automatically calculated. +# (usually 1/8 of writer-buffer-size, rounded up to a multiple of 4KB) +# Supported Units [K|M|G], arena-block-size default unit is in [bytes]. +arena-block-size : + +# Timeout of Pika's connection, counting down starts When there are no requests +# on a connection (it enters sleep state), when the countdown reaches 0, the connection +# will be closed by Pika. +# [Tip] The issue of running out of Pika's connections may be avoided if this value +# is configured properly. +# The Unit of timeout is in [seconds] and its default value is 60(s). timeout : 60 -# Requirepass + +# The [password of administrator], which is empty by default. +# [NOTICE] If this admin password is the same as user password (including both being empty), +# the value of userpass will be ignored and all users are considered as administrators, +# in this scenario, users are not subject to the restrictions imposed by the userblacklist. +# PS: "user password" refers to value of the parameter below: userpass. requirepass : -# Masterauth + +# Password for replication verify, used for authentication when a slave +# connects to a master to request replication. +# [NOTICE] The value of this parameter must match the "requirepass" setting on the master. masterauth : -# Userpass -userpass : -# User Blacklist + +# The [password of user], which is empty by default.(Deprecated) +# [NOTICE] If this user password is the same as admin password (including both being empty), +# the value of this parameter will be ignored and all users are considered as administrators, +# in this scenario, users are not subject to the restrictions imposed by the userblacklist. +# PS: "admin password" refers to value of the parameter above: requirepass. +# userpass : + +# The blacklist of commands for users that logged in by userpass, +# the commands that added to this list will not be available for users except for administrator. +# [Advice] It's recommended to add high-risk commands to this list. +# [Format] Commands should be separated by ",". For example: FLUSHALL, SHUTDOWN, KEYS, CONFIG +# By default, this list is empty. userblacklist : -# Dump Prefix + +# Running Mode of Pika, The current version only supports running in "classic mode". +# If set to 'classic', Pika will create multiple DBs whose number is the value of configure item "databases". +instance-mode : classic + +# The number of databases when Pika runs in classic mode. +# The default database id is DB 0. You can select a different one on +# a per-connection by using SELECT. The db id range is [0, 'databases' value -1]. +# The value range of this parameter is [1, 8]. +databases : 1 + +# The number of followers of a master. Only [0, 1, 2, 3, 4] is valid at present. +# By default, this num is set to 0, which means this feature is [not enabled] +# and the Pika runs in standalone mode. +replication-num : 0 + +# consensus level defines the num of confirms(ACKs) the leader node needs to receive from +# follower nodes before returning the result to the client that sent the request. +# The [value range] of this parameter is: [0, ...replicaiton-num]. +# The default value of consensus-level is 0, which means this feature is not enabled. +consensus-level : 0 + +# The Prefix of dump file's name. +# All the files that generated by command "bgsave" will be name with this prefix. dump-prefix : -# daemonize [yes | no] + +# daemonize [yes | no]. #daemonize : yes -# slotmigrate [yes | no] -#slotmigrate : no -# Dump Path + +# The directory to stored dump files that generated by command "bgsave". dump-path : ./dump/ -# Expire-dump-days + +# TTL of dump files that generated by command "bgsave". +# Any dump files which exceed this TTL will be deleted. +# Unit of dump-expire is in [days] and the default value is 0(day), +# which means dump files never expire. dump-expire : 0 -# pidfile Path + +# Pid file Path of Pika. pidfile : ./pika.pid -# Max Connection + +# The Maximum number of Pika's Connection. maxclients : 20000 -# the per file size of sst to compact, defalut is 2M -target-file-size-base : 20971520 -# Expire-logs-days + +# The size of sst file in RocksDB(Pika is based on RocksDB). +# sst files are hierarchical, the smaller the sst file size, the higher the performance and the lower the merge cost, +# the price is that the number of sst files could be huge. On the contrary, the bigger the sst file size, the lower +# the performance and the higher the merge cost, while the number of files is fewer. +# Supported Units [K|M|G], target-file-size-base default unit is in [bytes] and the default value is 20M. +target-file-size-base : 20M + +# Expire-time of binlog(write2file) files that stored within log-path. +# Any binlog(write2file) files that exceed this expire time will be cleaned up. +# The unit of expire-logs-days is in [days] and the default value is 7(days). +# The [Minimum value] of this parameter is 1(day). expire-logs-days : 7 -# Expire-logs-nums + +# The maximum number of binlog(write2file) files. +# Once the total number of binlog files exceed this value, +# automatic cleaning will start to ensure the maximum number +# of binlog files is equal to expire-logs-nums. +# The [Minimum value] of this parameter is 10. expire-logs-nums : 10 -# Root-connection-num + +# The number of guaranteed connections for root user. +# This parameter guarantees that there are 2(By default) connections available +# for root user to log in Pika from 127.0.0.1, even if the maximum connection limit is reached. +# PS: The maximum connection refers to the parameter above: maxclients. +# The default value of root-connection-num is 2. root-connection-num : 2 -# Slowlog-log-slower-than + +# Slowlog-write-errorlog +slowlog-write-errorlog : no + +# The time threshold for slow log recording. +# Any command whose execution time exceeds this threshold will be recorded in pika-ERROR.log, +# which is stored in log-path. +# The unit of slowlog-log-slower-than is in [microseconds(μs)] and the default value is 10000 μs / 10 ms. slowlog-log-slower-than : 10000 -# slave-read-only(yes/no, 1/0) -slave-read-only : 0 + +# Slowlog-max-len +slowlog-max-len : 128 + # Pika db sync path db-sync-path : ./dbsync/ -# db sync speed(MB) max is set to 125MB, min is set to 0, and if below 0 or above 125, the value will be adjust to 125 + +# The maximum Transmission speed during full synchronization. +# The exhaustion of network can be prevented by setting this parameter properly. +# The value range of this parameter is [1,1024] with unit in [MB/s]. +# [NOTICE] If this parameter is set to an invalid value(smaller than 0 or bigger than 1024), +# it will be automatically reset to 1024. +# The default value of db-sync-speed is -1 (1024MB/s). db-sync-speed : -1 -# network interface -# network-interface : eth1 -# replication -# slaveof : master-ip:master-port -# CronTask, format: start:end-ratio, like 02-04/60, pika will check to schedule compaction between 2 to 4 o'clock everyday -# if the freesize/disksize > 60% -# compact-cron : -################### -## Critical Settings -################### -# binlog file size: default is 100M, limited in [1K, 2G] +# The priority of slave node when electing new master node. +# The slave node with [lower] value of slave-priority will have [higher priority] to be elected as the new master node. +# This parameter is only used in conjunction with sentinel and serves no other purpose. +# The default value of slave-priority is 100. +slave-priority : 100 + +# Specify network interface that work with Pika. +#network-interface : eth1 + +# The IP and port of the master node are specified by this parameter for +# replication between master and slaves. +# [Format] is "ip:port" , for example: "192.168.1.2:6666" indicates that +# the slave instances that configured with this value will automatically send +# SLAVEOF command to port 6666 of 192.168.1.2 after startup. +# This parameter should be configured on slave nodes. +#slaveof : master-ip:master-port + + +# Daily/Weekly Automatic full compaction task is configured by compact-cron. +# +# [Format-daily]: start time(hour)-end time(hour)/disk-free-space-ratio, +# example: with value of "02-04/60", Pika will perform full compaction task between 2:00-4:00 AM everyday if +# the disk-free-size / disk-size > 60%. +# +# [Format-weekly]: week/start time(hour)-end time(hour)/disk-free-space-ratio, +# example: with value of "3/02-04/60", Pika will perform full compaction task between 2:00-4:00 AM every Wednesday if +# the disk-free-size / disk-size > 60%. +# +# [Tip] Automatic full compaction is suitable for scenarios with multiple data structures +# and lots of items are expired or deleted, or key names are frequently reused. +# +# [NOTICE]: If compact-interval is set, compact-cron will be masked and disabled. +# +#compact-cron : 3/02-04/60 + + +# Automatic full synchronization task between a time interval is configured by compact-interval. +# [Format]: time interval(hour)/disk-free-space-ratio, example: "6/60", Pika will perform full compaction every 6 hours, +# if the disk-free-size / disk-size > 60%. +# [NOTICE]: compact-interval is prior than compact-cron. +#compact-interval : + +# The minimum disk usage ratio for checking resume. +# If the disk usage ratio is lower than min-check-resume-ratio, it will not check resume, only higher will check resume. +# Its default value is 0.7. +#min-check-resume-ratio : 0.7 + +# The minimum free disk space to trigger db resume. +# If the db has a background error, only the free disk size is larger than this configuration can trigger manually resume db. +# Its default value is 256MB. +# [NOTICE]: least-free-disk-resume-size should not smaller than write-buffer-size! +#least-free-disk-resume-size : 256M + +# Manually trying to resume db interval is configured by manually-resume-interval. +# If db has a background error, it will try to manually call resume() to resume db if satisfy the least free disk to resume. +# Its default value is 60 seconds. +#manually-resume-interval : 60 + +# This window-size determines the amount of data that can be transmitted in a single synchronization process. +# [Tip] In the scenario of high network latency. Increasing this size can improve synchronization efficiency. +# Its default value is 9000. the [maximum] value is 90000. +sync-window-size : 9000 + +# Maximum buffer size of a client connection. +# Only three values are valid here: [67108864(64MB) | 268435456(256MB) | 536870912(512MB)]. +# [NOTICE] Master and slaves must have exactly the same value for the max-conn-rbuf-size. +# Supported Units [K|M|G]. Its default unit is in [bytes] and its default value is 268435456(256MB). +max-conn-rbuf-size : 268435456 + + +#######################################################################E####### +#! Critical Settings !# +#######################################################################E####### + +# write_binlog [yes | no] +write-binlog : yes + +# The size of binlog file, which can not be modified once Pika instance started. +# [NOTICE] Master and slaves must have exactly the same value for the binlog-file-size. +# The [value range] of binlog-file-size is [1K, 2G]. +# Supported Units [K|M|G], binlog-file-size default unit is in [bytes] and the default value is 100M. binlog-file-size : 104857600 -# Compression + +# Automatically triggers a small compaction according to statistics +# Use the cache to store up to 'max-cache-statistic-keys' keys +# If 'max-cache-statistic-keys' set to '0', that means turn off the statistics function +# and this automatic small compaction feature is disabled. +max-cache-statistic-keys : 0 + +# When 'delete' or 'overwrite' a specific multi-data structure key 'small-compaction-threshold' times, +# a small compact is triggered automatically if the small compaction feature is enabled. +# small-compaction-threshold default value is 5000 and the value range is [1, 100000]. +small-compaction-threshold : 5000 + +# The maximum total size of all live memtables of the RocksDB instance that owned by Pika. +# Flushing from memtable to disk will be triggered if the actual memory usage of RocksDB +# exceeds max-write-buffer-size when next write operation is issued. +# [RocksDB-Basic-Tuning](https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning) +# Supported Units [K|M|G], max-write-buffer-size default unit is in [bytes]. +max-write-buffer-size : 10737418240 + +# The maximum number of write buffers(memtables) that are built up in memory for one ColumnFamily in DB. +# The default and the minimum number is 2. It means that Pika(RocksDB) will write to a write buffer +# when it flushes the data of another write buffer to storage. +# If max-write-buffer-num > 3, writing will be slowed down. +max-write-buffer-num : 2 + +# The maximum size of the response package to client to prevent memory +# exhaustion caused by commands like 'keys *' and 'Scan' which can generate huge response. +# Supported Units [K|M|G]. The default unit is in [bytes]. +max-client-response-size : 1073741824 + +# The compression algorithm. You can not change it when Pika started. +# Supported types: [snappy, zlib, lz4, zstd]. If you do not wanna compress the SST file, please set its value as none. +# [NOTICE] The Pika official binary release just linking the snappy library statically, which means that +# you should compile the Pika from the source code and then link it with other compression algorithm library statically by yourself. compression : snappy -# max-background-flushes: default is 1, limited in [1, 4] + +# if the vector size is smaller than the level number, the undefined lower level uses the +# last option in the configurable array, for example, for 3 level +# LSM tree the following settings are the same: +# configurable array: [none:snappy] +# LSM settings: [none:snappy:snappy] +# When this configurable is enabled, compression is ignored, +# default l0 l1 noCompression, l2 and more use `compression` option +# https://github.com/facebook/rocksdb/wiki/Compression +#compression_per_level : [none:none:snappy:lz4:lz4] + +# The number of background flushing threads. +# max-background-flushes default value is 1 and the value range is [1, 4]. max-background-flushes : 1 -# max-background-compactions: default is 2, limited in [1, 4] + +# The number of background compacting threads. +# max-background-compactions default value is 2 and the value range is [1, 8]. max-background-compactions : 2 -# max-background-jobs: default is 3, limited in [2, 8] + +# The number of background threads. +# max-background-jobs default value is 3 and the value range is [2, 12]. max-background-jobs : 3 -# max-cache-files default is 5000 + +# maximum value of RocksDB cached open file descriptors max-cache-files : 5000 -# max_bytes_for_level_multiplier: default is 10, you can change it to 5 + +# The ratio between the total size of RocksDB level-(L+1) files and the total size of RocksDB level-L files for all L. +# Its default value is 10(x). You can also change it to 5(x). max-bytes-for-level-multiplier : 10 + +# slotmigrate is mainly used to migrate slots, usually we will set it to no. +# When you migrate slots, you need to set it to yes, and reload slotskeys before. +# slotmigrate [yes | no] +slotmigrate : no + +# BlockBasedTable block_size, default 4k +# block-size: 4096 + +# block LRU cache, default 8M, 0 to disable +# Supported Units [K|M|G], default unit [bytes] +# block-cache: 8388608 + +# num-shard-bits default -1, the number of bits from cache keys to be use as shard id. +# The cache will be sharded into 2^num_shard_bits shards. +# https://github.com/EighteenZi/rocksdb_wiki/blob/master/Block-Cache.md#lru-cache +# num-shard-bits: -1 + +# whether the block cache is shared among the RocksDB instances, default is per CF +# share-block-cache: no + +# The slot number of pika when used with codis. +default-slot-num : 1024 + +# whether or not index and filter blocks is stored in block cache +# cache-index-and-filter-blocks: no + +# pin_l0_filter_and_index_blocks_in_cache [yes | no] +# When `cache-index-and-filter-blocks` is enabled, `pin_l0_filter_and_index_blocks_in_cache` is suggested to be enabled +# pin_l0_filter_and_index_blocks_in_cache : no + +# when set to yes, bloomfilter of the last level will not be built +# optimize-filters-for-hits: no +# https://github.com/facebook/rocksdb/wiki/Leveled-Compaction#levels-target-size +# level-compaction-dynamic-level-bytes: no + +################################## RocksDB Rate Limiter ####################### +# rocksdb rate limiter +# https://rocksdb.org/blog/2017/12/18/17-auto-tuned-rate-limiter.html +# https://github.com/EighteenZi/rocksdb_wiki/blob/master/Rate-Limiter.md +#######################################################################E####### + +# rate limiter bandwidth, default 2000MB/s +#rate-limiter-bandwidth : 2097152000 + +#rate-limiter-refill-period-us : 100000 +# +#rate-limiter-fairness: 10 + +# rate limiter auto tune https://rocksdb.org/blog/2017/12/18/17-auto-tuned-rate-limiter.html. the default value is false. +#rate-limiter-auto-tuned : true + +################################## RocksDB Blob Configure ##################### +# rocksdb blob configure +# https://rocksdb.org/blog/2021/05/26/integrated-blob-db.html +# wiki https://github.com/facebook/rocksdb/wiki/BlobDB +#######################################################################E####### + +# enable rocksdb blob, default no +# enable-blob-files : yes + +# values at or above this threshold will be written to blob files during flush or compaction. +# Supported Units [K|M|G], default unit is in [bytes]. +# min-blob-size : 4K + +# the size limit for blob files +# Supported Units [K|M|G], default unit is in [bytes]. +# blob-file-size : 256M + +# the compression type to use for blob files. All blobs in the same file are compressed using the same algorithm. +# Supported types: [snappy, zlib, lz4, zstd]. If you do not wanna compress the SST file, please set its value as none. +# [NOTICE] The Pika official binary release just link the snappy library statically, which means that +# you should compile the Pika from the source code and then link it with other compression algorithm library statically by yourself. +# blob-compression-type : lz4 + +# set this to open to make BlobDB actively relocate valid blobs from the oldest blob files as they are encountered during compaction. +# The value option is [yes | no] +# enable-blob-garbage-collection : no + +# the cutoff that the GC logic uses to determine which blob files should be considered “old“. +# This parameter can be tuned to adjust the trade-off between write amplification and space amplification. +# blob-garbage-collection-age-cutoff : 0.25 + +# if the ratio of garbage in the oldest blob files exceeds this threshold, +# targeted compactions are scheduled in order to force garbage collecting the blob files in question +# blob_garbage_collection_force_threshold : 1.0 + +# the Cache object to use for blobs, default not open +# blob-cache : 0 + +# blob-num-shard-bits default -1, the number of bits from cache keys to be use as shard id. +# The cache will be sharded into 2^blob-num-shard-bits shards. +# blob-num-shard-bits : -1 + +# Rsync Rate limiting configuration 200MB/s +throttle-bytes-per-second : 207200000 +max-rsync-parallel-num : 4 + +# The synchronization mode of Pika primary/secondary replication is determined by ReplicationID. ReplicationID in one replication_cluster are the same +# replication-id : + +################### +## Cache Settings +################### +# the number of caches for every db +cache-num : 16 + +# cache-model 0:cache_none 1:cache_read +cache-model : 1 +# cache-type: string, set, zset, list, hash, bit +cache-type: string, set, zset, list, hash + +# Maximum number of keys in the zset redis cache +# On the disk DB, a zset field may have many fields. In the memory cache, we limit the maximum +# number of keys that can exist in a zset, which is zset-zset-cache-field-num-per-key, with a +# default value of 512. +zset-cache-field-num-per-key : 512 + +# If the number of elements in a zset in the DB exceeds zset-cache-field-num-per-key, +# we determine whether to cache the first 512[zset-cache-field-num-per-key] elements +# or the last 512[zset-cache-field-num-per-key] elements in the zset based on zset-cache-start-direction. +# +# If zset-cache-start-direction is 0, cache the first 512[zset-cache-field-num-per-key] elements from the header +# If zset-cache-start-direction is -1, cache the last 512[zset-cache-field-num-per-key] elements +zset-cache-start-direction : 0 + +# the cache maxmemory of every db, configuration 10G +cache-maxmemory : 10737418240 + +# cache-maxmemory-policy +# 0: volatile-lru -> Evict using approximated LRU among the keys with an expire set. +# 1: allkeys-lru -> Evict any key using approximated LRU. +# 2: volatile-lfu -> Evict using approximated LFU among the keys with an expire set. +# 3: allkeys-lfu -> Evict any key using approximated LFU. +# 4: volatile-random -> Remove a random key among the ones with an expire set. +# 5: allkeys-random -> Remove a random key, any key. +# 6: volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# 7: noeviction -> Don't evict anything, just return an error on write operations. +cache-maxmemory-policy : 1 + +# cache-maxmemory-samples +cache-maxmemory-samples: 5 + +# cache-lfu-decay-time +cache-lfu-decay-time: 1 + + +# is possible to manage access to Pub/Sub channels with ACL rules as well. The +# default Pub/Sub channels permission if new users is controlled by the +# acl-pubsub-default configuration directive, which accepts one of these values: +# +# allchannels: grants access to all Pub/Sub channels +# resetchannels: revokes access to all Pub/Sub channels +# +# acl-pubsub-default defaults to 'resetchannels' permission. +# acl-pubsub-default : resetchannels + +# ACL users are defined in the following format: +# user : ... acl rules ... +# +# For example: +# +# user : worker on >password ~key* +@all + +# Using an external ACL file +# +# Instead of configuring users here in this file, it is possible to use +# a stand-alone file just listing users. The two methods cannot be mixed: +# if you configure users here and at the same time you activate the external +# ACL file, the server will refuse to start. +# +# The format of the external ACL user file is exactly the same as the +# format that is used inside pika.conf to describe users. +# +# aclfile : ../conf/users.acl + diff --git a/tests/assets/nodefaultuser.acl b/tests/assets/nodefaultuser.acl new file mode 100644 index 0000000000..2557c7fe74 --- /dev/null +++ b/tests/assets/nodefaultuser.acl @@ -0,0 +1,2 @@ +user alice on nopass ~* +@all +user bob on nopass ~* &* +@all \ No newline at end of file diff --git a/tests/assets/user.acl b/tests/assets/user.acl new file mode 100644 index 0000000000..926ac54f6f --- /dev/null +++ b/tests/assets/user.acl @@ -0,0 +1,3 @@ +user alice on allcommands allkeys &* >alice +user bob on -@all +@set +acl ~set* &* >bob +user default on nopass ~* &* +@all diff --git a/tests/assets/userwithselectors.acl b/tests/assets/userwithselectors.acl new file mode 100644 index 0000000000..5d42957d3f --- /dev/null +++ b/tests/assets/userwithselectors.acl @@ -0,0 +1,2 @@ +user alice on (+get ~rw*) +user bob on (+set %W~w*) (+get %R~r*) \ No newline at end of file diff --git a/tests/conf/pika.conf b/tests/conf/pika.conf index 1bc2ddd99a..7c0c0f791d 100644 --- a/tests/conf/pika.conf +++ b/tests/conf/pika.conf @@ -9,7 +9,10 @@ port : 9221 # Random value identifying the Pika server, its string length must be 40. # If not set, Pika will generate a random string with a length of 40 random characters. -# run-id: +# run-id : + +# Master's run-id +# master-run-id : # The number of threads for running Pika. # It's not recommended to set this value exceeds @@ -65,12 +68,12 @@ requirepass : # [NOTICE] The value of this parameter must match the "requirepass" setting on the master. masterauth : -# The [password of user], which is empty by default. +# The [password of user], which is empty by default.(Deprecated) # [NOTICE] If this user password is the same as admin password (including both being empty), # the value of this parameter will be ignored and all users are considered as administrators, # in this scenario, users are not subject to the restrictions imposed by the userblacklist. # PS: "admin password" refers to value of the parameter above: requirepass. -userpass : +# userpass : # The blacklist of commands for users that logged in by userpass, # the commands that added to this list will not be available for users except for administrator. @@ -105,7 +108,7 @@ consensus-level : 0 dump-prefix : # daemonize [yes | no]. -daemonize : yes +#daemonize : yes # The directory to stored dump files that generated by command "bgsave". dump-path : ./dump/ @@ -214,6 +217,22 @@ slave-priority : 100 # [NOTICE]: compact-interval is prior than compact-cron. #compact-interval : +# The minimum disk usage ratio for checking resume. +# If the disk usage ratio is lower than min-check-resume-ratio, it will not check resume, only higher will check resume. +# Its default value is 0.7. +#min-check-resume-ratio : 0.7 + +# The minimum free disk space to trigger db resume. +# If the db has a background error, only the free disk size is larger than this configuration can trigger manually resume db. +# Its default value is 256MB. +# [NOTICE]: least-free-disk-resume-size should not smaller than write-buffer-size! +#least-free-disk-resume-size : 256M + +# Manually trying to resume db interval is configured by manually-resume-interval. +# If db has a background error, it will try to manually call resume() to resume db if satisfy the least free disk to resume. +# Its default value is 60 seconds. +#manually-resume-interval : 60 + # This window-size determines the amount of data that can be transmitted in a single synchronization process. # [Tip] In the scenario of high network latency. Increasing this size can improve synchronization efficiency. # Its default value is 9000. the [maximum] value is 90000. @@ -344,8 +363,8 @@ default-slot-num : 1024 # https://github.com/EighteenZi/rocksdb_wiki/blob/master/Rate-Limiter.md #######################################################################E####### -# rate limiter bandwidth, default 200MB -#rate-limiter-bandwidth : 209715200 +# rate limiter bandwidth, default 2000MB/s +#rate-limiter-bandwidth : 2097152000 #rate-limiter-refill-period-us : 100000 # @@ -395,3 +414,86 @@ default-slot-num : 1024 # blob-num-shard-bits default -1, the number of bits from cache keys to be use as shard id. # The cache will be sharded into 2^blob-num-shard-bits shards. # blob-num-shard-bits : -1 + +# Rsync Rate limiting configuration 200MB/s +throttle-bytes-per-second : 207200000 +max-rsync-parallel-num : 4 + +# The synchronization mode of Pika primary/secondary replication is determined by ReplicationID. ReplicationID in one replication_cluster are the same +# replication-id : + +################### +## Cache Settings +################### +# the number of caches for every db +cache-num : 16 + +# cache-model 0:cache_none 1:cache_read +cache-model : 1 +# cache-type: string, set, zset, list, hash, bit +cache-type: string, set, zset, list, hash + +# Maximum number of keys in the zset redis cache +# On the disk DB, a zset field may have many fields. In the memory cache, we limit the maximum +# number of keys that can exist in a zset, which is zset-zset-cache-field-num-per-key, with a +# default value of 512. +zset-cache-field-num-per-key : 512 + +# If the number of elements in a zset in the DB exceeds zset-cache-field-num-per-key, +# we determine whether to cache the first 512[zset-cache-field-num-per-key] elements +# or the last 512[zset-cache-field-num-per-key] elements in the zset based on zset-cache-start-direction. +# +# If zset-cache-start-direction is 0, cache the first 512[zset-cache-field-num-per-key] elements from the header +# If zset-cache-start-direction is -1, cache the last 512[zset-cache-field-num-per-key] elements +zset-cache-start-direction : 0 + +# the cache maxmemory of every db, configuration 10G +cache-maxmemory : 10737418240 + +# cache-maxmemory-policy +# 0: volatile-lru -> Evict using approximated LRU among the keys with an expire set. +# 1: allkeys-lru -> Evict any key using approximated LRU. +# 2: volatile-lfu -> Evict using approximated LFU among the keys with an expire set. +# 3: allkeys-lfu -> Evict any key using approximated LFU. +# 4: volatile-random -> Remove a random key among the ones with an expire set. +# 5: allkeys-random -> Remove a random key, any key. +# 6: volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# 7: noeviction -> Don't evict anything, just return an error on write operations. +cache-maxmemory-policy : 1 + +# cache-maxmemory-samples +cache-maxmemory-samples: 5 + +# cache-lfu-decay-time +cache-lfu-decay-time: 1 + + +# is possible to manage access to Pub/Sub channels with ACL rules as well. The +# default Pub/Sub channels permission if new users is controlled by the +# acl-pubsub-default configuration directive, which accepts one of these values: +# +# allchannels: grants access to all Pub/Sub channels +# resetchannels: revokes access to all Pub/Sub channels +# +# acl-pubsub-default defaults to 'resetchannels' permission. +# acl-pubsub-default : resetchannels + +# ACL users are defined in the following format: +# user : ... acl rules ... +# +# For example: +# +# user : worker on >password ~key* +@all + +# Using an external ACL file +# +# Instead of configuring users here in this file, it is possible to use +# a stand-alone file just listing users. The two methods cannot be mixed: +# if you configure users here and at the same time you activate the external +# ACL file, the server will refuse to start. +# +# The format of the external ACL user file is exactly the same as the +# format that is used inside pika.conf to describe users. +# +# aclfile : ../conf/users.acl + diff --git a/tests/instances.tcl b/tests/instances.tcl index 426508f33a..d087a8192d 100644 --- a/tests/instances.tcl +++ b/tests/instances.tcl @@ -405,3 +405,9 @@ proc restart_instance {type id} { set_instance_attrib $type $id link $link } +proc redis_deferring_client {type id} { + set port [get_instance_attrib $type $id port] + set host [get_instance_attrib $type $id host] + set client [redis $host $port 1 $::tls] + return $client +} \ No newline at end of file diff --git a/tests/integration/server_test.go b/tests/integration/server_test.go index 813f19b966..9c21767928 100644 --- a/tests/integration/server_test.go +++ b/tests/integration/server_test.go @@ -50,12 +50,12 @@ var _ = Describe("Server", func() { Expect(r.Val()).To(Equal("OK")) r = client.Do(ctx, "AUTH", "wrong!") - Expect(r.Err()).To(MatchError("ERR invalid password")) + Expect(r.Err()).To(MatchError("WRONGPASS invalid username-password pair or user is disabled.")) - r = client.Do(ctx, "AUTH", "foo", "bar") - Expect(r.Err()).To(MatchError("ERR wrong number of arguments for 'auth' command")) + // r = client.Do(ctx, "AUTH", "foo", "bar") + // Expect(r.Err()).To(MatchError("ERR wrong number of arguments for 'auth' command")) - r = client.Do(ctx, "AUTH", "foobar") + r = client.Do(ctx, "AUTH", "default", "foobar") Expect(r.Val()).To(Equal("OK")) r = client.Do(ctx, "config", "set", "requirepass", "") @@ -73,7 +73,7 @@ var _ = Describe("Server", func() { _, err1 := cmds[0].(*redis.MapStringInterfaceCmd).Result() m2, err2 := cmds[1].(*redis.MapStringInterfaceCmd).Result() m3, err3 := cmds[2].(*redis.MapStringInterfaceCmd).Result() - Expect(err1).To(MatchError("ERR -NOPROTO unsupported protocol version")) + Expect(err1).To(MatchError("NOPROTO unsupported protocol version")) Expect(err2).NotTo(HaveOccurred()) Expect(m2["proto"]).To(Equal(int64(2))) @@ -93,11 +93,11 @@ var _ = Describe("Server", func() { r = client.Do(ctx, "config", "set", "requirepass", "foobar") Expect(r.Val()).To(Equal("OK")) - r = client.Do(ctx, "hello", "3", "auth", "wrong") - Expect(r.Err()).To(MatchError("ERR invalid password")) - - r = client.Do(ctx, "hello", "3", "auth", "foobar") - Expect(r.Err()).NotTo(HaveOccurred()) + // r = client.Do(ctx, "hello", "3", "auth", "wrong") + // Expect(r.Err()).To(MatchError("ERR invalid password")) + // + // r = client.Do(ctx, "hello", "3", "auth", "foobar") + // Expect(r.Err()).NotTo(HaveOccurred()) r = client.Do(ctx, "config", "set", "requirepass", "") }) diff --git a/tests/support/test.tcl b/tests/support/test.tcl index 767b9f0862..d9b81f2d9a 100644 --- a/tests/support/test.tcl +++ b/tests/support/test.tcl @@ -13,6 +13,28 @@ proc assert {condition} { } } +proc assert_no_match {pattern value} { + if {[string match $pattern $value]} { + set context "(context: [info frame -1])" + error "assertion:Expected '$value' to not match '$pattern' $context" + } +} + +proc assert_failed {expected_err detail} { + if {$detail ne ""} { + set detail "(detail: $detail)" + } else { + set detail "(context: [info frame -2])" + } + error "assertion:$expected_err $detail" +} + +proc assert_not_equal {value expected {detail ""}} { + if {!($expected ne $value)} { + assert_failed "Expected '$value' not equal to '$expected'" $detail + } +} + proc assert_match {pattern value} { if {![string match $pattern $value]} { error "assertion:Expected '$value' to match '$pattern'" diff --git a/tests/support/util.tcl b/tests/support/util.tcl index f1d4853221..cc628784a2 100644 --- a/tests/support/util.tcl +++ b/tests/support/util.tcl @@ -376,3 +376,25 @@ proc populate {size} { r set "key:$counter" "key:$counter" } } + +proc wait_for_blocked_client {{idx 0}} { + wait_for_condition 50 100 { + [s $idx blocked_clients] ne 0 + } else { + fail "no blocked clients" + } +} + +# Shuffle a list with Fisher-Yates algorithm. +proc lshuffle {list} { + set n [llength $list] + while {$n>1} { + set j [expr {int(rand()*$n)}] + incr n -1 + if {$n==$j} continue + set v [lindex $list $j] + lset list $j [lindex $list $n] + lset list $n $v + } + return $list +} \ No newline at end of file diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 25722a95c6..37730c648c 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -51,6 +51,7 @@ set ::all_tests { # unit/hyperloglog # unit/command unit/type + unit/acl } # because the comment not works in tcl list, use regsub to ignore the item starting with '#' @@ -153,7 +154,7 @@ proc redis_deferring_client {args} { set client [redis [srv $level "host"] [srv $level "port"] 1] # select the right db and read the response (OK) - $client select 9 + $client select 0 $client read return $client } diff --git a/tests/unit/acl.tcl b/tests/unit/acl.tcl new file mode 100644 index 0000000000..20900905fc --- /dev/null +++ b/tests/unit/acl.tcl @@ -0,0 +1,1135 @@ +start_server {tags {"acl external:skip"}} { + test {Connections start with the default user} { + r ACL WHOAMI + } {default} + + test {It is possible to create new users} { + r ACL setuser newuser + } + + test {Coverage: ACL USERS} { + r ACL USERS + } {default newuser} + + test {Usernames can not contain spaces or null characters} { + catch {r ACL setuser "a a"} err + set err + } {*Usernames can't contain spaces or null characters*} + + test {New users start disabled} { + r ACL setuser newuser >passwd1 + catch {r AUTH newuser passwd1} err + set err + } {*WRONGPASS*} + + test {Enabling the user allows the login} { + r ACL setuser newuser on +acl + r AUTH newuser passwd1 + r ACL WHOAMI + } {newuser} + + test {Only the set of correct passwords work} { + r ACL setuser newuser >passwd2 + catch {r AUTH newuser passwd1} e + assert {$e eq "OK"} + catch {r AUTH newuser passwd2} e + assert {$e eq "OK"} + catch {r AUTH newuser passwd3} e + set e + } {*WRONGPASS*} + + test {It is possible to remove passwords from the set of valid ones} { + r ACL setuser newuser pspass +acl +client +@pubsub + r AUTH psuser pspass + catch {r PUBLISH foo bar} e + set e + } {*NOPERM*channel*} + + test {By default, only default user is able to subscribe to any channel} { + set rd [redis_deferring_client] + $rd AUTH default pwd + $rd read + $rd SUBSCRIBE foo + assert_match {subscribe foo 1} [$rd read] + $rd UNSUBSCRIBE + $rd read + $rd AUTH psuser pspass + $rd read + $rd SUBSCRIBE foo + catch {$rd read} e + $rd close + set e + } {*NOPERM*channel*} + + test {By default, only default user is able to subscribe to any pattern} { + set rd [redis_deferring_client] + $rd AUTH default pwd + $rd read + $rd PSUBSCRIBE bar* + assert_match {psubscribe bar\* 1} [$rd read] + $rd PUNSUBSCRIBE + $rd read + $rd AUTH psuser pspass + $rd read + $rd PSUBSCRIBE bar* + catch {$rd read} e + $rd close + set e + } {*NOPERM*channel*} + + test {It's possible to allow publishing to a subset of channels} { + r ACL setuser psuser resetchannels &foo:1 &bar:* + assert_equal {0} [r PUBLISH foo:1 somemessage] + assert_equal {0} [r PUBLISH bar:2 anothermessage] + catch {r PUBLISH zap:3 nosuchmessage} e + set e + } {*NOPERM*channel*} + + test {Validate subset of channels is prefixed with resetchannels flag} { + r ACL setuser hpuser on nopass resetchannels &foo +@all + + # Verify resetchannels flag is prefixed before the channel name(s) + set users [r ACL LIST] + set curruser "hpuser" + + # authenticate as hpuser + r AUTH hpuser pass + + assert_equal {0} [r PUBLISH foo bar] + catch {r PUBLISH bar game} e + + # Falling back to psuser for the below tests + r AUTH psuser pspass + r ACL deluser hpuser + set e + } {*NOPERM*channel*} + + test {In transaction queue publish/subscribe/psubscribe to unauthorized channel will fail} { + r ACL setuser psuser +multi +discard + r MULTI + assert_error {*NOPERM*channel*} {r PUBLISH notexits helloworld} + r DISCARD + r MULTI + assert_error {*NOPERM*channel*} {r SUBSCRIBE notexits foo:1} + r DISCARD + r MULTI + assert_error {*NOPERM*channel*} {r PSUBSCRIBE notexits:* bar:*} + r DISCARD + } + + test {It's possible to allow subscribing to a subset of channels} { + set rd [redis_deferring_client] + $rd AUTH psuser pspass + $rd read + $rd SUBSCRIBE foo:1 + assert_match {subscribe foo:1 1} [$rd read] + $rd SUBSCRIBE bar:2 + assert_match {subscribe bar:2 2} [$rd read] + $rd SUBSCRIBE zap:3 + catch {$rd read} e + set e + } {*NOPERM*channel*} + +# test {It's possible to allow subscribing to a subset of shard channels} { +# set rd [redis_deferring_client] +# $rd AUTH psuser pspass +# $rd read +# $rd SSUBSCRIBE foo:1 +# assert_match {ssubscribe foo:1 1} [$rd read] +# $rd SSUBSCRIBE bar:2 +# assert_match {ssubscribe bar:2 2} [$rd read] +# $rd SSUBSCRIBE zap:3 +# catch {$rd read} e +# set e +# } {*NOPERM*channel*} + + test {It's possible to allow subscribing to a subset of channel patterns} { + set rd [redis_deferring_client] + $rd AUTH psuser pspass + $rd read + $rd PSUBSCRIBE foo:1 + assert_match {psubscribe foo:1 1} [$rd read] + $rd PSUBSCRIBE bar:* + assert_match {psubscribe bar:\* 2} [$rd read] + $rd PSUBSCRIBE bar:baz + catch {$rd read} e + set e + } {*NOPERM*channel*} + + test {Subscribers are killed when revoked of channel permission} { + set rd [redis_deferring_client] + r ACL setuser psuser resetchannels &foo:1 + $rd AUTH psuser pspass + $rd read + $rd CLIENT SETNAME deathrow + $rd read + $rd SUBSCRIBE foo:1 + $rd read + r ACL setuser psuser resetchannels + assert_no_match {*deathrow*} [r CLIENT LIST] + $rd close + } {0} + +# test {Subscribers are killed when revoked of channel permission} { +# set rd [redis_deferring_client] +# r ACL setuser psuser resetchannels &foo:1 +# $rd AUTH psuser pspass +# $rd read +# $rd CLIENT SETNAME deathrow +# $rd read +# $rd SSUBSCRIBE foo:1 +# $rd read +# r ACL setuser psuser resetchannels +# assert_no_match {*deathrow*} [r CLIENT LIST] +# $rd close +# } {0} + + test {Subscribers are killed when revoked of pattern permission} { + set rd [redis_deferring_client] + r ACL setuser psuser resetchannels &bar:* + $rd AUTH psuser pspass + $rd read + $rd CLIENT SETNAME deathrow + $rd read + $rd PSUBSCRIBE bar:* + $rd read + r ACL setuser psuser resetchannels + assert_no_match {*deathrow*} [r CLIENT LIST] + $rd close + } {0} + + test {Subscribers are killed when revoked of allchannels permission} { + set rd [redis_deferring_client] + r ACL setuser psuser allchannels + $rd AUTH psuser pspass + $rd read + $rd CLIENT SETNAME deathrow + $rd read + $rd PSUBSCRIBE foo + $rd read + r ACL setuser psuser resetchannels + assert_no_match {*deathrow*} [r CLIENT LIST] + $rd close + } {0} + +# test {Subscribers are pardoned if literal permissions are retained and/or gaining allchannels} { +# set rd [redis_deferring_client] +# r ACL setuser psuser resetchannels &foo:1 &bar:* &orders +# $rd AUTH psuser pspass +# $rd read +# $rd CLIENT SETNAME pardoned +# $rd read +# $rd SUBSCRIBE foo:1 +# $rd read +# $rd SSUBSCRIBE orders +# $rd read +# $rd PSUBSCRIBE bar:* +# $rd read +# r ACL setuser psuser resetchannels &foo:1 &bar:* &orders &baz:qaz &zoo:* +# assert_match {*pardoned*} [r CLIENT LIST] +# r ACL setuser psuser allchannels +# assert_match {*pardoned*} [r CLIENT LIST] +# $rd close +# } {0} + +### +# test {blocked command gets rejected when reprocessed after permission change} { +# r auth default "" +# r config resetstat +# set rd [redis_deferring_client] +# r ACL setuser psuser reset on nopass +@all allkeys +# $rd AUTH psuser pspass +# $rd read +# $rd BLPOP list1 0 +# wait_for_blocked_client +# r ACL setuser psuser resetkeys +# r LPUSH list1 foo +# assert_error {*NOPERM No permissions to access a key*} {$rd read} +# $rd ping +# $rd close +# assert_match {*calls=0,usec=0,*,rejected_calls=1,failed_calls=0} [cmdrstat blpop r] +# } + + test {Users can be configured to authenticate with any password} { + r ACL setuser newuser nopass + r AUTH newuser zipzapblabla + } {OK} + + test {ACLs can exclude single commands} { + r ACL setuser newuser -ping + r INCR mycounter ; # Should not raise an error + catch {r PING} e + set e + } {*NOPERM*ping*} + + test {ACLs can include or exclude whole classes of commands} { + r ACL setuser newuser -@all +@set +acl + r SADD myset a b c; # Should not raise an error + r ACL setuser newuser +@all -@string + r SADD myset a b c; # Again should not raise an error + # String commands instead should raise an error + catch {r SET foo bar} e + r ACL setuser newuser allcommands; # Undo commands ACL + set e + } {*NOPERM*set*} + + test {ACLs can include single subcommands} { + r ACL setuser newuser +@all -client + r ACL setuser newuser +client|setname + set cmdstr [dict get [r ACL getuser newuser] commands] + #assert_match {+@all*-client*+client|id*} $cmdstr + assert_match {+@all*-client*+client|setname*} $cmdstr + #r CLIENT ID; # Should not fail + r CLIENT SETNAME foo ; # Should not fail + catch {r CLIENT KILL ALL} e + set e + } {*NOPERM*client|kill*} + + test {ACLs can exclude single subcommands, case 1} { + r ACL setuser newuser +@all -client|kill + set cmdstr [dict get [r ACL getuser newuser] commands] + assert_equal {+@all -client|kill} $cmdstr + #r CLIENT ID; # Should not fail + r CLIENT SETNAME foo ; # Should not fail + catch {r CLIENT KILL all} e + set e + } {*NOPERM*client|kill*} + + test {ACLs can exclude single subcommands, case 2} { + r ACL setuser newuser -@all +acl +config -config|set + set cmdstr [dict get [r ACL getuser newuser] commands] + assert_match {*+config*} $cmdstr + assert_match {*-config|set*} $cmdstr + r CONFIG GET loglevel; # Should not fail + catch {r CONFIG SET loglevel debug} e + set e + } {*NOPERM*config|set*} + + test {ACLs cannot include a subcommand with a specific arg} { + r ACL setuser newuser +@all -config|get + catch { r ACL setuser newuser +config|get|appendonly} e + set e + } {*Allowing first-arg of a subcommand is not supported*} + + test {ACLs cannot exclude or include a container commands with a specific arg} { + r ACL setuser newuser +@all +config|get + catch { r ACL setuser newuser +@all +config|asdf} e + assert_match "*Unknown command or category name in ACL*" $e + catch { r ACL setuser newuser +@all -config|asdf} e + assert_match "*Unknown command or category name in ACL*" $e + } {} + +# test {ACLs cannot exclude or include a container command with two args} { +# r ACL setuser newuser +@all +config|get +# catch { r ACL setuser newuser +@all +get|key1|key2} e +# assert_match "*Unknown command or category name in ACL*" $e +# catch { r ACL setuser newuser +@all -get|key1|key2} e +# assert_match "*Unknown command or category name in ACL*" $e +# } {} + +# now pika not supported the command +# test {ACLs including of a type includes also subcommands} { +# r ACL setuser newuser -@all +del +acl +@stream +# r DEL key +# r XADD key * field value +# r XINFO STREAM key +# } + +# test {ACLs can block all DEBUG subcommands except one} { +# r ACL setuser newuser -@all +acl +del +incr +debug|object +# r DEL key +# set cmdstr [dict get [r ACL getuser newuser] commands] +# assert_match {*+debug|object*} $cmdstr +# r INCR key +# r DEBUG OBJECT key +# catch {r DEBUG SEGFAULT} e +# set e +# } {*NOPERM*debug*} + +# test {ACLs set can include subcommands, if already full command exists} { +# r ACL setuser bob +memory|doctor +# set cmdstr [dict get [r ACL getuser bob] commands] +# assert_equal {-@all +memory|doctor} $cmdstr +# +# # Validate the commands have got engulfed to +memory. +# r ACL setuser bob +memory +# set cmdstr [dict get [r ACL getuser bob] commands] +# assert_equal {-@all +memory} $cmdstr +# +# # Appending to the existing access string of bob. +# r ACL setuser bob +@all +client|id +# # Although this does nothing, we retain it anyways so we can reproduce +# # the original ACL. +# set cmdstr [dict get [r ACL getuser bob] commands] +# assert_equal {+@all +client|id} $cmdstr +# +# r ACL setuser bob >passwd1 on +# r AUTH bob passwd1 +# r CLIENT ID; # Should not fail +# r MEMORY DOCTOR; # Should not fail +# } + +# now pika not supported the command +# test {ACLs set can exclude subcommands, if already full command exists} { +# r ACL setuser alice +@all -memory|doctor +# set cmdstr [dict get [r ACL getuser alice] commands] +# assert_equal {+@all -memory|doctor} $cmdstr +# +# r ACL setuser alice >passwd1 on +# r AUTH alice passwd1 +# +# assert_error {*NOPERM*memory|doctor*} {r MEMORY DOCTOR} +# r MEMORY STATS ;# should work +# +# # Validate the commands have got engulfed to -memory. +# r ACL setuser alice +@all -memory +# set cmdstr [dict get [r ACL getuser alice] commands] +# assert_equal {+@all -memory} $cmdstr +# +# assert_error {*NOPERM*memory|doctor*} {r MEMORY DOCTOR} +# assert_error {*NOPERM*memory|stats*} {r MEMORY STATS} +# +# # Appending to the existing access string of alice. +# r ACL setuser alice -@all +# +# # Now, alice can't do anything, we need to auth newuser to execute ACL GETUSER +# r AUTH newuser passwd1 +# +# # Validate the new commands has got engulfed to -@all. +# set cmdstr [dict get [r ACL getuser alice] commands] +# assert_equal {-@all} $cmdstr +# +# r AUTH alice passwd1 +# +# assert_error {*NOPERM*get*} {r GET key} +# assert_error {*NOPERM*memory|stats*} {r MEMORY STATS} +# +# # Auth newuser before the next test +# r AUTH newuser passwd1 +# } + + test {ACL SETUSER RESET reverting to default newly created user} { + set current_user "example" + r ACL DELUSER $current_user + r ACL SETUSER $current_user + + set users [r ACL LIST] + foreach user [lshuffle $users] { + if {[string first $current_user $user] != -1} { + set current_user_output $user + } + } + + r ACL SETUSER $current_user reset + set users [r ACL LIST] + foreach user [lshuffle $users] { + if {[string first $current_user $user] != -1} { + assert_equal $current_user_output $user + } + } + } + + # Note that the order of the generated ACL rules is not stable in Redis + # so we need to match the different parts and not as a whole string. + test {ACL GETUSER is able to translate back command permissions} { + # Subtractive + # r ACL setuser newuser reset +@all ~* -@string +incr -debug +debug|digest + r ACL setuser newuser reset +@all ~* -@string +incr + set cmdstr [dict get [r ACL getuser newuser] commands] + assert_match {*+@all*} $cmdstr + assert_match {*-@string*} $cmdstr + assert_match {*+incr*} $cmdstr + #assert_match {*-debug +debug|digest**} $cmdstr + + # Additive + #r ACL setuser newuser reset +@string -incr +acl +debug|digest +debug|segfault + r ACL setuser newuser reset +@string -incr +acl + set cmdstr [dict get [r ACL getuser newuser] commands] + assert_match {*-@all*} $cmdstr + assert_match {*+@string*} $cmdstr + assert_match {*-incr*} $cmdstr + # {*+debug|digest*} $cmdstr + #assert_match {*+debug|segfault*} $cmdstr + assert_match {*+acl*} $cmdstr + } + + # A regression test make sure that as long as there is a simple + # category defining the commands, that it will be used as is. + test {ACL GETUSER provides reasonable results} { + set categories [r ACL CAT] + + # Test that adding each single category will + # result in just that category with both +@all and -@all + foreach category $categories { + # Test for future commands where allowed + r ACL setuser additive reset +@all "-@$category" + set cmdstr [dict get [r ACL getuser additive] commands] + assert_equal "+@all -@$category" $cmdstr + + # Test for future commands where disallowed + r ACL setuser restrictive reset -@all "+@$category" + set cmdstr [dict get [r ACL getuser restrictive] commands] + assert_equal "-@all +@$category" $cmdstr + } + } + + # Test that only lossless compaction of ACLs occur. + test {ACL GETUSER provides correct results} { + r ACL SETUSER adv-test + r ACL SETUSER adv-test +@all -@hash -@slow +hget + assert_equal "+@all -@hash -@slow +hget" [dict get [r ACL getuser adv-test] commands] + + # Categories are re-ordered if re-added + r ACL SETUSER adv-test -@hash + assert_equal "+@all -@slow +hget -@hash" [dict get [r ACL getuser adv-test] commands] + + # Inverting categories removes existing categories + r ACL SETUSER adv-test +@hash + assert_equal "+@all -@slow +hget +@hash" [dict get [r ACL getuser adv-test] commands] + + # Inverting the all category compacts everything + r ACL SETUSER adv-test -@all + assert_equal "-@all" [dict get [r ACL getuser adv-test] commands] + r ACL SETUSER adv-test -@string -@slow +@all + assert_equal "+@all" [dict get [r ACL getuser adv-test] commands] + + # Make sure categories are case insensitive + r ACL SETUSER adv-test -@all +@HASH +@hash +@HaSh + assert_equal "-@all +@hash" [dict get [r ACL getuser adv-test] commands] + + # Make sure commands are case insensitive + r ACL SETUSER adv-test -@all +HGET +hget +hGeT + assert_equal "-@all +hget" [dict get [r ACL getuser adv-test] commands] + + # Arbitrary category additions and removals are handled + r ACL SETUSER adv-test -@all +@hash +@slow +@set +@set +@slow +@hash + assert_equal "-@all +@set +@slow +@hash" [dict get [r ACL getuser adv-test] commands] + + # Arbitrary command additions and removals are handled + r ACL SETUSER adv-test -@all +hget -hset +hset -hget + assert_equal "-@all +hset -hget" [dict get [r ACL getuser adv-test] commands] + + # Arbitrary subcommands are compacted + r ACL SETUSER adv-test -@all +client|list +client|list +config|get +config +acl|list -acl + assert_equal "-@all +client|list +config -acl" [dict get [r ACL getuser adv-test] commands] + + # Unnecessary categories are retained for potentional future compatibility (pika not supported `dangerous`) + #r ACL SETUSER adv-test -@all -@dangerous + #assert_equal "-@all -@dangerous" [dict get [r ACL getuser adv-test] commands] + + # Duplicate categories are compressed, regression test for #12470 + r ACL SETUSER adv-test -@all +config +config|get -config|set +config + assert_equal "-@all +config" [dict get [r ACL getuser adv-test] commands] + } + + test "ACL CAT with illegal arguments" { + assert_error {*Unknown category 'NON_EXISTS'} {r ACL CAT NON_EXISTS} + assert_error {*unknown subcommand or wrong number of arguments for 'CAT'*} {r ACL CAT NON_EXISTS NON_EXISTS2} + } + + test "ACL CAT without category - list all categories" { + set categories [r acl cat] + assert_not_equal [lsearch $categories "keyspace"] -1 + assert_not_equal [lsearch $categories "connection"] -1 + } + + test "ACL CAT category - list all commands/subcommands that belong to category" { + # now pika not supported the command + #assert_not_equal [lsearch [r acl cat transaction] "multi"] -1 + #assert_not_equal [lsearch [r acl cat scripting] "function|list"] -1 + + # Negative check to make sure it doesn't actually return all commands. + assert_equal [lsearch [r acl cat keyspace] "set"] -1 + #assert_equal [lsearch [r acl cat stream] "get"] -1 + } + +# now pika not supported the command +# test "ACL requires explicit permission for scripting for EVAL_RO, EVALSHA_RO and FCALL_RO" { +# r ACL SETUSER scripter on nopass +readonly +# assert_match {*has no permissions to run the 'eval_ro' command*} [r ACL DRYRUN scripter EVAL_RO "" 0] +# assert_match {*has no permissions to run the 'evalsha_ro' command*} [r ACL DRYRUN scripter EVALSHA_RO "" 0] +# assert_match {*has no permissions to run the 'fcall_ro' command*} [r ACL DRYRUN scripter FCALL_RO "" 0] +# } + +# now pika not supported the command +# test {ACL #5998 regression: memory leaks adding / removing subcommands} { +# r AUTH default "" +# r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c +# r ACL setuser newuser -debug +# # The test framework will detect a leak if any. +# } + +# now pika not supported the command +# test {ACL LOG aggregates similar errors together and assigns unique entry-id to new errors} { +# r ACL LOG RESET +# r ACL setuser user1 >foo +# assert_error "*WRONGPASS*" {r AUTH user1 doo} +# set entry_id_initial_error [dict get [lindex [r ACL LOG] 0] entry-id] +# set timestamp_created_original [dict get [lindex [r ACL LOG] 0] timestamp-created] +# set timestamp_last_update_original [dict get [lindex [r ACL LOG] 0] timestamp-last-updated] +# after 1 +# for {set j 0} {$j < 10} {incr j} { +# assert_error "*WRONGPASS*" {r AUTH user1 doo} +# } +# set entry_id_lastest_error [dict get [lindex [r ACL LOG] 0] entry-id] +# set timestamp_created_updated [dict get [lindex [r ACL LOG] 0] timestamp-created] +# set timestamp_last_updated_after_update [dict get [lindex [r ACL LOG] 0] timestamp-last-updated] +# assert {$entry_id_lastest_error eq $entry_id_initial_error} +# assert {$timestamp_last_update_original < $timestamp_last_updated_after_update} +# assert {$timestamp_created_original eq $timestamp_created_updated} +# r ACL setuser user2 >doo +# assert_error "*WRONGPASS*" {r AUTH user2 foo} +# set new_error_entry_id [dict get [lindex [r ACL LOG] 0] entry-id] +# assert {$new_error_entry_id eq $entry_id_lastest_error + 1 } +# } +# + test {ACL LOG shows failed command executions at toplevel} { + r ACL LOG RESET + r ACL setuser antirez >foo on +set ~object:1234 + r ACL setuser antirez +multi +exec + r ACL setuser antirez resetchannels +publish + r AUTH antirez foo + assert_error "*NOPERM*get*" {r GET foo} + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry username] eq {antirez}} + assert {[dict get $entry context] eq {toplevel}} + assert {[dict get $entry reason] eq {command}} + assert {[dict get $entry object] eq {get}} + assert_match {*cmd=get*} [dict get $entry client-info] + } + +# test "ACL LOG shows failed subcommand executions at toplevel" { +# r ACL LOG RESET +# r ACL DELUSER demo +# r ACL SETUSER demo on nopass +# r AUTH demo "" +# assert_error "*NOPERM*script|help*" {r SCRIPT HELP} +# r AUTH default "" +# set entry [lindex [r ACL LOG] 0] +# assert_equal [dict get $entry username] {demo} +# assert_equal [dict get $entry context] {toplevel} +# assert_equal [dict get $entry reason] {command} +# assert_equal [dict get $entry object] {script|help} +# } + + test {ACL LOG is able to test similar events} { + r ACL LOG RESET + r AUTH antirez foo + catch {r GET foo} + catch {r GET foo} + catch {r GET foo} + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry count] == 3} + } + + test {ACL LOG is able to log keys access violations and key name} { + r AUTH antirez foo + catch {r SET somekeynotallowed 1234} + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry reason] eq {key}} + assert {[dict get $entry object] eq {somekeynotallowed}} + } + + test {ACL LOG is able to log channel access violations and channel name} { + r AUTH antirez foo + catch {r PUBLISH somechannelnotallowed nullmsg} + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry reason] eq {channel}} + assert {[dict get $entry object] eq {somechannelnotallowed}} + } + + test {ACL LOG RESET is able to flush the entries in the log} { + r ACL LOG RESET + assert {[llength [r ACL LOG]] == 0} + } + + test {ACL LOG can distinguish the transaction context (1)} { + r AUTH antirez foo + r MULTI + catch {r INCR foo} + catch {r EXEC} + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry context] eq {multi}} + assert {[dict get $entry object] eq {incr}} + } + + test {ACL LOG can distinguish the transaction context (2)} { + set rd1 [redis_deferring_client] + r ACL SETUSER antirez +incr + + r AUTH antirez foo + r MULTI + r INCR object:1234 + $rd1 ACL SETUSER antirez -incr + $rd1 read + catch {r EXEC} + $rd1 close + r AUTH default "" + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry context] eq {multi}} + assert {[dict get $entry object] eq {incr}} + r ACL SETUSER antirez -incr + } + +# now pika not supported lua command +# test {ACL can log errors in the context of Lua scripting} { +# r AUTH antirez foo +# catch {r EVAL {redis.call('incr','foo')} 0} +# r AUTH default "" +# set entry [lindex [r ACL LOG] 0] +# assert {[dict get $entry context] eq {lua}} +# assert {[dict get $entry object] eq {incr}} +# assert_match {*cmd=eval*} [dict get $entry client-info] +# } + + test {ACL LOG can accept a numerical argument to show less entries} { + r AUTH antirez foo + catch {r INCR foo} + catch {r INCR foo} + catch {r INCR foo} + catch {r INCR foo} + r AUTH default "" + assert {[llength [r ACL LOG]] > 1} + assert {[llength [r ACL LOG 2]] == 2} + } + + test {ACL LOG can log failed auth attempts} { + catch {r AUTH antirez wrong-password} + set entry [lindex [r ACL LOG] 0] + assert {[dict get $entry context] eq {toplevel}} + assert {[dict get $entry reason] eq {auth}} + assert {[dict get $entry object] eq {AUTH}} + assert {[dict get $entry username] eq {antirez}} + } + + test {ACL LOG entries are limited to a maximum amount} { + r ACL LOG RESET + r CONFIG SET acllog-max-len 5 + r AUTH antirez foo + for {set j 0} {$j < 10} {incr j} { + catch {r SET obj:$j 123} + } + r AUTH default "" + assert {[llength [r ACL LOG]] == 5} + } + +# test {When default user is off, new connections are not authenticated} { +# r ACL setuser default off +# catch {set rd1 [redis_deferring_client]} e +# r ACL setuser default on +# set e +# } {*NOAUTH*} + + test {When default user has no command permission, hello command still works for other users} { + r ACL setuser secure-user >supass on +@all + r ACL setuser default -@all + r HELLO 2 AUTH secure-user supass + r ACL setuser default nopass +@all + r AUTH default "" + } + + test {When an authentication chain is used in the HELLO cmd, the last auth cmd has precedence} { + r ACL setuser secure-user1 >supass on +@all + r ACL setuser secure-user2 >supass on +@all + r HELLO 2 AUTH secure-user supass AUTH secure-user2 supass AUTH secure-user1 supass + assert_equal [r ACL whoami] {secure-user1} + catch {r HELLO 2 AUTH secure-user supass AUTH secure-user2 supass AUTH secure-user pass} e + assert_match "WRONGPASS invalid username-password pair or user is disabled." $e + assert_equal [r ACL whoami] {secure-user2} + } + + test {When a setname chain is used in the HELLO cmd, the last setname cmd has precedence} { + r HELLO 2 setname client1 setname client2 setname client3 setname client4 + assert_equal [r client getname] {client4} + catch {r HELLO 2 setname client5 setname client6 setname "client name"} e + assert_match "ERR Client names cannot contain spaces, newlines or special characters." $e + assert_equal [r client getname] {client6} + } + + test {When authentication fails in the HELLO cmd, the client setname should not be applied} { + r client setname client0 + catch {r HELLO 2 AUTH user pass setname client1} e + assert_match "WRONGPASS invalid username-password pair or user is disabled." $e + assert {[r client getname] eq {client0}} + } + + test {ACL HELP should not have unexpected options} { + catch {r ACL help xxx} e + assert_match "*wrong number of arguments for 'acl|help' command" $e + } + + test {Delete a user that the client doesn't use} { + r ACL setuser not_used on >passwd + assert {[r ACL deluser not_used] == 1} + # The client is not closed + assert {[r ping] eq {PONG}} + } + + test {Delete a user that the client is using} { + r ACL setuser using on +acl >passwd + r AUTH using passwd + # The client will receive reply normally + assert {[r ACL deluser using] == 1} + # The client is closed + catch {[r ping]} e + assert_match "*I/O error*" $e + } + + test {ACL GENPASS command failed test} { + catch {r ACL genpass -236} err1 + catch {r ACL genpass 5000} err2 + assert_match "*ACL GENPASS argument must be the number*" $err1 + assert_match "*ACL GENPASS argument must be the number*" $err2 + } + + test {Default user can not be removed} { + catch {r ACL deluser default} err + set err + } {ERR The 'default' user cannot be removed} + + test {ACL load non-existing configured ACL file} { + catch {r ACL load} err + set err + } {*not configured to use an ACL file*} + + # If there is an AUTH failure the metric increases +# test {ACL-Metrics user AUTH failure} { +# set current_auth_failures [s acl_access_denied_auth] +# set current_invalid_cmd_accesses [s acl_access_denied_cmd] +# set current_invalid_key_accesses [s acl_access_denied_key] +# set current_invalid_channel_accesses [s acl_access_denied_channel] +# assert_error "*WRONGPASS*" {r AUTH notrealuser 1233456} +# assert {[s acl_access_denied_auth] eq [expr $current_auth_failures + 1]} +# assert_error "*WRONGPASS*" {r HELLO 3 AUTH notrealuser 1233456} +# assert {[s acl_access_denied_auth] eq [expr $current_auth_failures + 2]} +# assert_error "*WRONGPASS*" {r HELLO 2 AUTH notrealuser 1233456} +# assert {[s acl_access_denied_auth] eq [expr $current_auth_failures + 3]} +# assert {[s acl_access_denied_cmd] eq $current_invalid_cmd_accesses} +# assert {[s acl_access_denied_key] eq $current_invalid_key_accesses} +# assert {[s acl_access_denied_channel] eq $current_invalid_channel_accesses} +# } +# +# # If a user try to access an unauthorized command the metric increases +# test {ACL-Metrics invalid command accesses} { +# set current_auth_failures [s acl_access_denied_auth] +# set current_invalid_cmd_accesses [s acl_access_denied_cmd] +# set current_invalid_key_accesses [s acl_access_denied_key] +# set current_invalid_channel_accesses [s acl_access_denied_channel] +# r ACL setuser invalidcmduser on >passwd nocommands +# r AUTH invalidcmduser passwd +# assert_error "*no permissions to run the * command*" {r acl list} +# r AUTH default "" +# assert {[s acl_access_denied_auth] eq $current_auth_failures} +# assert {[s acl_access_denied_cmd] eq [expr $current_invalid_cmd_accesses + 1]} +# assert {[s acl_access_denied_key] eq $current_invalid_key_accesses} +# assert {[s acl_access_denied_channel] eq $current_invalid_channel_accesses} +# } +# +# # If a user try to access an unauthorized key the metric increases +# test {ACL-Metrics invalid key accesses} { +# set current_auth_failures [s acl_access_denied_auth] +# set current_invalid_cmd_accesses [s acl_access_denied_cmd] +# set current_invalid_key_accesses [s acl_access_denied_key] +# set current_invalid_channel_accesses [s acl_access_denied_channel] +# r ACL setuser invalidkeyuser on >passwd resetkeys allcommands +# r AUTH invalidkeyuser passwd +# assert_error "*NOPERM*key*" {r get x} +# r AUTH default "" +# assert {[s acl_access_denied_auth] eq $current_auth_failures} +# assert {[s acl_access_denied_cmd] eq $current_invalid_cmd_accesses} +# assert {[s acl_access_denied_key] eq [expr $current_invalid_key_accesses + 1]} +# assert {[s acl_access_denied_channel] eq $current_invalid_channel_accesses} +# } +# +# # If a user try to access an unauthorized channel the metric increases +# test {ACL-Metrics invalid channels accesses} { +# set current_auth_failures [s acl_access_denied_auth] +# set current_invalid_cmd_accesses [s acl_access_denied_cmd] +# set current_invalid_key_accesses [s acl_access_denied_key] +# set current_invalid_channel_accesses [s acl_access_denied_channel] +# r ACL setuser invalidchanneluser on >passwd resetchannels allcommands +# r AUTH invalidkeyuser passwd +# assert_error "*NOPERM*channel*" {r subscribe x} +# r AUTH default "" +# assert {[s acl_access_denied_auth] eq $current_auth_failures} +# assert {[s acl_access_denied_cmd] eq $current_invalid_cmd_accesses} +# assert {[s acl_access_denied_key] eq $current_invalid_key_accesses} +# assert {[s acl_access_denied_channel] eq [expr $current_invalid_channel_accesses + 1]} +# } +} + +set server_path [tmpdir "server.acl"] +set base_path ${server_path}/ +exec cp -f tests/assets/user.acl $base_path +set acl_file ${base_path}user.acl +start_server [list overrides [list "dir" $server_path "acl-pubsub-default" "allchannels" "aclfile" $acl_file ] tags [list "external:skip"]] { + # user alice on allcommands allkeys &* >alice + # user bob on -@all +@set +acl ~set* &* >bob + # user default on nopass ~* &* +@all + + test {default: load from include file, can access any channels} { + r SUBSCRIBE foo + r PSUBSCRIBE bar* + r UNSUBSCRIBE + r PUNSUBSCRIBE + r PUBLISH hello world + } + + test {default: with config acl-pubsub-default allchannels after reset, can access any channels} { + r ACL setuser default reset on nopass ~* +@all + r SUBSCRIBE foo + r PSUBSCRIBE bar* + r UNSUBSCRIBE + r PUNSUBSCRIBE + r PUBLISH hello world + } + + test {default: with config acl-pubsub-default resetchannels after reset, can not access any channels} { + r CONFIG SET acl-pubsub-default resetchannels + r ACL setuser default reset on nopass ~* +@all + assert_error {*NOPERM*channel*} {r SUBSCRIBE foo} + assert_error {*NOPERM*channel*} {r PSUBSCRIBE bar*} + assert_error {*NOPERM*channel*} {r PUBLISH hello world} + r CONFIG SET acl-pubsub-default resetchannels + } + + test {Alice: can execute all command} { + r AUTH alice alice + assert_equal "alice" [r acl whoami] + r SET key value + } + + test {Bob: just execute @set and acl command} { + r AUTH bob bob + assert_equal "bob" [r acl whoami] + # The test was passed on local machine, Restarting the pika data will still exist, + # which may cause the test to fail, so remove it + #assert_equal "3" [r sadd set 1 2 3] + catch {r SET key value} e + set e + } {*NOPERM*set*} + + test {ACL load and save} { + r ACL setuser eve +get allkeys >eve on + r ACL save + + # ACL load will free user and kill clients + r ACL load + catch {r ACL LIST} e + assert_match {*I/O error*} $e + + reconnect + r AUTH alice alice + r SET key value + r AUTH eve eve + r GET key + catch {r SET key value} e + set e + } {*NOPERM*set*} + + test {ACL load and save with restricted channels} { + r AUTH alice alice + r ACL setuser harry on nopass resetchannels &test +@all ~* + r ACL save + + # ACL load will free user and kill clients + r ACL load + catch {r ACL LIST} e + assert_match {*I/O error*} $e + + reconnect + r AUTH harry anything + r publish test bar + catch {r publish test1 bar} e + r ACL deluser harry + set e + } {*NOPERM*channel*} +} + +set server_path [tmpdir "resetchannels.acl"] +set base_path ${server_path}/ +exec cp -f tests/assets/nodefaultuser.acl $base_path +exec cp -f tests/assets/default.conf $server_path +set acl_file ${base_path}nodefaultuser.acl +start_server [list overrides [list "dir" $server_path "aclfile" $acl_file] tags [list "external:skip"]] { + + test {Default user has access to all channels irrespective of flag} { + set channelinfo [dict get [r ACL getuser default] channels] + assert_equal "&*" $channelinfo + set channelinfo [dict get [r ACL getuser alice] channels] + assert_equal "" $channelinfo + } + + test {Update acl-pubsub-default, existing users shouldn't get affected} { + set channelinfo [dict get [r ACL getuser default] channels] + assert_equal "&*" $channelinfo + r CONFIG set acl-pubsub-default allchannels + r ACL setuser mydefault + set channelinfo [dict get [r ACL getuser mydefault] channels] + assert_equal "&*" $channelinfo + r CONFIG set acl-pubsub-default resetchannels + set channelinfo [dict get [r ACL getuser mydefault] channels] + assert_equal "&*" $channelinfo + } + + test {Single channel is valid} { + r ACL setuser onechannel &test + set channelinfo [dict get [r ACL getuser onechannel] channels] + assert_equal "&test" $channelinfo + r ACL deluser onechannel + } + + test {Single channel is not valid with allchannels} { + r CONFIG set acl-pubsub-default allchannels + catch {r ACL setuser onechannel &test} err + r CONFIG set acl-pubsub-default resetchannels + set err + } {*start with an empty list of channels*} +} + +set server_path [tmpdir "resetchannels.acl"] +set base_path ${server_path}/ +exec cp -f tests/assets/nodefaultuser.acl $base_path +exec cp -f tests/assets/default.conf $server_path +set acl_file ${base_path}nodefaultuser.acl +start_server [list overrides [list "dir" $server_path "acl-pubsub-default" "resetchannels" "aclfile" $acl_file] tags [list "external:skip"]] { + + test {Only default user has access to all channels irrespective of flag} { + set channelinfo [dict get [r ACL getuser default] channels] + assert_equal "&*" $channelinfo + set channelinfo [dict get [r ACL getuser alice] channels] + assert_equal "" $channelinfo + } +} + + +start_server {overrides {user "default on nopass ~* +@all"} tags {"external:skip"}} { + test {default: load from config file, without channel permission default user can't access any channels} { + catch {r SUBSCRIBE foo} e + set e + } {*NOPERM*channel*} +} + +start_server {overrides {user "default on nopass ~* &* +@all"} tags {"external:skip"}} { + test {default: load from config file with all channels permissions} { + r SUBSCRIBE foo + r PSUBSCRIBE bar* + r UNSUBSCRIBE + r PUNSUBSCRIBE + r PUBLISH hello world + } +} + +set server_path [tmpdir "duplicate.acl"] +set base_path ${server_path}/ +exec cp -f tests/assets/user.acl $base_path +exec cp -f tests/assets/default.conf $server_path +set acl_file ${base_path}user.acl +start_server [list overrides [list "dir" $server_path "aclfile" $acl_file] tags [list "external:skip"]] { + + test {Test loading an ACL file with duplicate users} { + exec cp -f tests/assets/user.acl $base_path + + # Corrupt the ACL file + set corruption "\nuser alice on nopass ~* -@all" + exec echo $corruption >> ${base_path}user.acl + catch {r ACL LOAD} err + assert_match {*Duplicate user 'alice' found*} $err + + # Verify the previous users still exist + # NOTE: A missing user evaluates to an empty + # string. + assert {[r ACL GETUSER alice] != ""} + assert_equal [dict get [r ACL GETUSER alice] commands] "+@all" + assert {[r ACL GETUSER bob] != ""} + assert {[r ACL GETUSER default] != ""} + } + + test {Test loading an ACL file with duplicate default user} { + exec cp -f tests/assets/user.acl $base_path + + # Corrupt the ACL file + set corruption "\nuser default on nopass ~* -@all" + exec echo $corruption >> ${base_path}user.acl + catch {r ACL LOAD} err + assert_match {*Duplicate user 'default' found*} $err + + # Verify the previous users still exist + # NOTE: A missing user evaluates to an empty + # string. + assert {[r ACL GETUSER alice] != ""} + assert_equal [dict get [r ACL GETUSER alice] commands] "+@all" + assert {[r ACL GETUSER bob] != ""} + assert {[r ACL GETUSER default] != ""} + } +} + +# test on local machine is passed +#Because the tcl test was slow and there was a problem with restarting the service, everything was removed +#start_server {overrides {user "default on nopass ~* +@all -flushdb"} tags {acl external:skip}} { +# test {ACL from config file and config rewrite} { +# assert_error {NOPERM *} {r flushdb} +# r config rewrite +# restart_server 0 true false +# assert_error {NOPERM *} {r flushdb} +# } +#}