Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(AclFamily): add acl commands #1844

Merged
merged 13 commits into from
Sep 15, 2023
Merged

feat(AclFamily): add acl commands #1844

merged 13 commits into from
Sep 15, 2023

Conversation

kostasrim
Copy link
Contributor

@kostasrim kostasrim commented Sep 11, 2023

  • Add acl commands
  • Add tests
  • Extend validation to work with acl commands
  • Extend acl load/save to work with acl commands
  • Extend acl list to work with acl commands
  • Move helper functions used for parsing in acl family to a separate file

resolves #1809

@kostasrim kostasrim marked this pull request as ready for review September 12, 2023 10:54
@kostasrim
Copy link
Contributor Author

kostasrim commented Sep 12, 2023

@kostasrim kostasrim assigned adiholden and unassigned adiholden Sep 12, 2023
@kostasrim kostasrim self-assigned this Sep 12, 2023
@kostasrim
Copy link
Contributor Author

@dranikpg Do not hate me, this is the only large PR that was left. It's mostly boilerplate, so you don't have to worry 😛

Maybe I could have move the utility functions in AclFamily.cc in another PR but it was such a pain to have them in the same file so I moved them here

@@ -850,6 +850,8 @@ void BitOpsFamily::Register(CommandRegistry* registry) {
<< CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1, acl::kBitOp}.SetHandler(&BitOp)
<< CI{"GETBIT", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kGetBit}.SetHandler(&GetBit)
<< CI{"SETBIT", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1, acl::kSetBit}.SetHandler(&SetBit);

builder | "BITPOS" | "BITCOUNT" | "BITFIELD" | "BITFIELD_RO" | "BITOP" | "GETBIT" | "SETBIT";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dranikpg I Know what your thoughts are here. Probably you would comment that why don't you move this to the Command constructor such that we get the strings naturally. The reason is:

  1. It requires passing the builder to the construction of the CI objects. That means that we make the same amount of line changes
  2. I think it's better this way because it decouples it. If we push everything in the constructor of the CI it will be painfully to work with all these parameters
  3. The only downside is that I might misspelled a command name. I trust my copy pasting ninja skills so I would say that this won't happen. If it happens we will get a bug report and fix this quickly. Either way not a big deal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You guessed correctly. Duplicating all command names yet a third time is hilarious

I'd argue there is a muuuch better approach. The CommandRegistry is not a relict of the past - change it as you like. Lets add the notion of families there. Lets change it to a CommandRegistryBuilder if it becomes too much.

In short, I'd like to keep existing code as simple as possible. It is possible, without changing all the familes and passing the acl stuff around to keep track of families.

Let then your builder access that information from the command registry. Also if you have it already prepared, you don't need a full blown builder for it

using RevCommandField = std::vector<std::string>;
using RevCommandsIndexStore = std::vector<RevCommandField>;

class CommandTableBuilder {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bread and butter of this PR. This allows us to build adhoc the index table for commands, since it's so large and I didn't even bother hardcoding the index structure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot the letter i in Pair

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah you have a typedef one line above, matched the wrong line

@kostasrim
Copy link
Contributor Author

I am also planning some small refactoring of the common logic found in helpers.hpp/cpp but that will be done in a separate PR. This is too large, I don't want to make it even harder for reviewing

@kostasrim kostasrim changed the title [WIP - do not review] feat(AclFamily): add acl commands feat(AclFamily): add acl commands Sep 12, 2023
src/facade/dragonfly_connection.cc Show resolved Hide resolved
using RevCommandField = std::vector<std::string>;
using RevCommandsIndexStore = std::vector<RevCommandField>;

class CommandTableBuilder {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot the letter i in Pair

src/server/acl/acl_family.cc Show resolved Hide resolved
src/server/acl/acl_family.cc Show resolved Hide resolved
Comment on lines 86 to 94
void User::SetAclCommands(size_t index, uint64_t bit_index, bool all) {
if (all) {
for (auto& family : commands_) {
family = ALL_COMMANDS;
}
return;
}
commands_[index] |= bit_index;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its kinda confusing that with the all argument your index & bit_index have no meaning

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on this, let me see what I can do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using the index, if it's numeric_limits::max() then it denotes an all. I also added a comment

@@ -850,6 +850,8 @@ void BitOpsFamily::Register(CommandRegistry* registry) {
<< CI{"BITOP", CO::WRITE | CO::NO_AUTOJOURNAL, -4, 2, -1, 1, acl::kBitOp}.SetHandler(&BitOp)
<< CI{"GETBIT", CO::READONLY | CO::FAST, 3, 1, 1, 1, acl::kGetBit}.SetHandler(&GetBit)
<< CI{"SETBIT", CO::WRITE | CO::DENYOOM, 4, 1, 1, 1, acl::kSetBit}.SetHandler(&SetBit);

builder | "BITPOS" | "BITCOUNT" | "BITFIELD" | "BITFIELD_RO" | "BITOP" | "GETBIT" | "SETBIT";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You guessed correctly. Duplicating all command names yet a third time is hilarious

I'd argue there is a muuuch better approach. The CommandRegistry is not a relict of the past - change it as you like. Lets add the notion of families there. Lets change it to a CommandRegistryBuilder if it becomes too much.

In short, I'd like to keep existing code as simple as possible. It is possible, without changing all the familes and passing the acl stuff around to keep track of families.

Let then your builder access that information from the command registry. Also if you have it already prepared, you don't need a full blown builder for it

Comment on lines 123 to 131
friend CommandTableBuilder& operator|(CommandTableBuilder& builder, std::string name) {
(*builder.index_)[name] = {builder.pos_, 1ULL << (builder.bit_number_++)};
(*builder.rindex_)[builder.pos_].push_back(std::move(name));
// We should crash if somehow we end up feeding more than the bitfield size
// This won't happen in production, since our tests will fail and we will never
// merge something that violates this
CHECK_LT(++builder.bits_limit, size_t(64));
return builder;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the difference between the builder and a single function that accepts an absl::Span<string_view> 😆

But as I suggested above, lets not duplicate everything a third time. Lets finally make the command registry itself usable and then you don't need 800 lines of changes to do this all

@dranikpg
Copy link
Contributor

Does redis actually do the same with bitfields?

What if I persist search settings and now a new command is added in-between? All the bit indices become displaced and the settings are wrong noq

@kostasrim
Copy link
Contributor Author

Does redis actually do the same with bitfields?

Persistence does not write the index structure on the file but rather the full command that was used to create the user. Therefore, we won't ever have to deal with such problem

dranikpg
dranikpg previously approved these changes Sep 14, 2023
Copy link
Contributor

@dranikpg dranikpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll approve to not delay this PR any longer, but please see my comments. Briefly, I suggest fully removing the CommandsIndexer and store family id / command it in the commands itself. I don't see any downsides and it makes it much simpler

src/server/acl/acl_commands_def.h Outdated Show resolved Hide resolved
src/server/acl/acl_family.cc Outdated Show resolved Hide resolved
src/server/conn_context.cc Show resolved Hide resolved
Comment on lines 17 to 20
const auto& commands = acl::CommandsIndexer();
DCHECK(commands.contains(id.name()));
auto [index, command_mask] = commands.find(id.name())->second;
DCHECK_LT(index, cntx.acl_commands.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on a second thought 😅 we don't need any of the global stores if we store the family index & command index in the CommandId itself and as a bonus we save a hashmap lookup... I don't see any downsides in storing this info there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good idea, I will get back on this. I won't merge this PR. Let's make it as good as possible.

Copy link
Contributor

@dranikpg dranikpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smallest nit


family_of_commands_.back().push_back(std::string(k));
cmd.SetFamily(family_of_commands_.size() - 1);
cmd.SetBitIndex(1ULL << bit_index_++);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then its a bit mask 🙂 actually you can store only the index and return 1<<i in get bit mask

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's a bit mask! Don't hate me, too lazy to make this change and I don't want to do a shift every time I access it :P (not that it matters though)

@kostasrim kostasrim merged commit bbd4c6b into main Sep 15, 2023
10 checks passed
@kostasrim kostasrim deleted the acl_part_10_add_commands branch September 15, 2023 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add filtering by commands
3 participants