mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
feat(AclFamily): add acl dryrun command (#1894)
* add acl dryrun command * add unit tests
This commit is contained in:
parent
b1c0d5c2a9
commit
ea589db4db
5 changed files with 81 additions and 8 deletions
|
@ -31,6 +31,7 @@
|
|||
#include "server/acl/acl_commands_def.h"
|
||||
#include "server/acl/acl_log.h"
|
||||
#include "server/acl/helpers.h"
|
||||
#include "server/acl/validator.h"
|
||||
#include "server/command_registry.h"
|
||||
#include "server/common.h"
|
||||
#include "server/conn_context.h"
|
||||
|
@ -455,6 +456,35 @@ void AclFamily::GetUser(CmdArgList args, ConnectionContext* cntx) {
|
|||
(*cntx)->SendSimpleString(acl);
|
||||
}
|
||||
|
||||
void AclFamily::DryRun(CmdArgList args, ConnectionContext* cntx) {
|
||||
auto username = facade::ArgS(args, 0);
|
||||
const auto registry_with_lock = registry_->GetRegistryWithLock();
|
||||
const auto& registry = registry_with_lock.registry;
|
||||
if (!registry.contains(username)) {
|
||||
auto error = absl::StrCat("User: ", username, " does not exists!");
|
||||
(*cntx)->SendError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ToUpper(&args[1]);
|
||||
auto command = facade::ArgS(args, 1);
|
||||
auto* cid = cmd_registry_->Find(command);
|
||||
if (!cid) {
|
||||
auto error = absl::StrCat("Command: ", command, " does not exists!");
|
||||
(*cntx)->SendError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& user = registry.find(username)->second;
|
||||
if (IsUserAllowedToInvokeCommandGeneric(user.AclCategory(), user.AclCommandsRef(), *cid)) {
|
||||
(*cntx)->SendOk();
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = absl::StrCat("User: ", username, " is not allowed to execute command: ", command);
|
||||
(*cntx)->SendError(error);
|
||||
}
|
||||
|
||||
using MemberFunc = void (AclFamily::*)(CmdArgList args, ConnectionContext* cntx);
|
||||
|
||||
CommandId::Handler HandlerFunc(AclFamily* acl, MemberFunc f) {
|
||||
|
@ -474,6 +504,7 @@ constexpr uint32_t kLog = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
|
|||
constexpr uint32_t kUsers = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
|
||||
constexpr uint32_t kCat = acl::SLOW;
|
||||
constexpr uint32_t kGetUser = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
|
||||
constexpr uint32_t kDryRun = acl::ADMIN | acl::SLOW | acl::DANGEROUS;
|
||||
|
||||
// We can't implement the ACL commands and its respective subcommands LIST, CAT, etc
|
||||
// the usual way, (that is, one command called ACL which then dispatches to the subcommand
|
||||
|
@ -507,6 +538,8 @@ void AclFamily::Register(dfly::CommandRegistry* registry) {
|
|||
Cat);
|
||||
*registry << CI{"ACL GETUSER", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, 2, 0, 0, 0, acl::kGetUser}
|
||||
.HFUNC(GetUser);
|
||||
*registry << CI{"ACL DRYRUN", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, 3, 0, 0, 0, acl::kDryRun}
|
||||
.HFUNC(DryRun);
|
||||
|
||||
cmd_registry_ = registry;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class AclFamily final {
|
|||
void Users(CmdArgList args, ConnectionContext* cntx);
|
||||
void Cat(CmdArgList args, ConnectionContext* cntx);
|
||||
void GetUser(CmdArgList args, ConnectionContext* cntx);
|
||||
void DryRun(CmdArgList args, ConnectionContext* cntx);
|
||||
|
||||
// Helper function that updates all open connections and their
|
||||
// respective ACL fields on all the available proactor threads
|
||||
|
|
|
@ -242,4 +242,34 @@ TEST_F(AclFamilyTest, TestGetUser) {
|
|||
EXPECT_THAT(kvec[5], "+@STRING +HSET");
|
||||
}
|
||||
|
||||
TEST_F(AclFamilyTest, TestDryRun) {
|
||||
TestInitAclFam();
|
||||
auto resp = Run({"ACL", "DRYRUN"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR wrong number of arguments for 'acl dryrun' command"));
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "default"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR wrong number of arguments for 'acl dryrun' command"));
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "default", "get", "more"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR wrong number of arguments for 'acl dryrun' command"));
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "kostas", "more"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR User: kostas does not exists!"));
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "default", "nope"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR Command: NOPE does not exists!"));
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "default", "SET"});
|
||||
EXPECT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"ACL", "SETUSER", "kostas", "+GET"});
|
||||
EXPECT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "kostas", "GET"});
|
||||
EXPECT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"ACL", "DRYRUN", "kostas", "SET"});
|
||||
EXPECT_THAT(resp, ErrArg("ERR User: kostas is not allowed to execute command: SET"));
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -13,13 +13,8 @@ namespace dfly::acl {
|
|||
|
||||
[[nodiscard]] bool IsUserAllowedToInvokeCommand(const ConnectionContext& cntx,
|
||||
const facade::CommandId& id) {
|
||||
const auto cat_credentials = id.acl_categories();
|
||||
|
||||
const size_t index = id.GetFamily();
|
||||
const uint64_t command_mask = id.GetBitIndex();
|
||||
DCHECK_LT(index, cntx.acl_commands.size());
|
||||
const bool is_authed = (cntx.acl_categories & cat_credentials) != 0 ||
|
||||
(cntx.acl_commands[index] & command_mask) != 0;
|
||||
const bool is_authed =
|
||||
IsUserAllowedToInvokeCommandGeneric(cntx.acl_categories, cntx.acl_commands, id);
|
||||
|
||||
if (!is_authed) {
|
||||
auto& log = ServerState::tlocal()->acl_log;
|
||||
|
@ -30,4 +25,14 @@ namespace dfly::acl {
|
|||
return is_authed;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsUserAllowedToInvokeCommandGeneric(uint32_t acl_cat,
|
||||
const std::vector<uint64_t>& acl_commands,
|
||||
const facade::CommandId& id) {
|
||||
const auto cat_credentials = id.acl_categories();
|
||||
const size_t index = id.GetFamily();
|
||||
const uint64_t command_mask = id.GetBitIndex();
|
||||
DCHECK_LT(index, acl_commands.size());
|
||||
return (acl_cat & cat_credentials) != 0 || (acl_commands[index] & command_mask) != 0;
|
||||
}
|
||||
|
||||
} // namespace dfly::acl
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
namespace dfly::acl {
|
||||
|
||||
bool IsUserAllowedToInvokeCommandGeneric(uint32_t acl_cat,
|
||||
const std::vector<uint64_t>& acl_commands,
|
||||
const facade::CommandId& id);
|
||||
|
||||
bool IsUserAllowedToInvokeCommand(const ConnectionContext& cntx, const facade::CommandId& id);
|
||||
|
||||
}
|
||||
} // namespace dfly::acl
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue