The problem/use-case that the feature addresses

There are cases that most of a selector's key patterns are same to the root selector, so there are many duplicated key patterns between selectors in ACL rules. This feature introduces new keywords to simplify the ACL rule.

For example, if we want to give a user all write permissions on k1 and k2, and give all write permission except del on k3, we can use the following ACL rule:

acl setuser u on >pass +@write ~k1 ~k2 (+@write -del ~k1 ~k2 ~k3)

Notice in the selector we have to set (~k1 ~k2 ~k3), because if we only set (~k3), the user will not have permission to execute a write command(not del) on both k1, k2 and k3(for example, it have no permission to execute mset k1 v1 k2 v2 k3 v3).

In this case we have to copy all the patterns from the root selector, and if the key patterns in the root selector are more complex, then it will also be more complicated to write the ACL rules.

Description of the feature

This feature introduces a new keyword named rootkeys which is used in a selector to indicate that this selector has inherited all the key patterns from the root selector. For example, the earlier ACL rule can be written as:

acl setuser u on >pass +@write ~k1 ~k2 (+@write -del rootkeys ~k3)

The rootkeys keyword can only be used inside a selector.

This feature also introduces another keyword +@root. It is used in a selector to indicate that this selector has inherited all the permissions from the root selector, so the earlier ACL rule can be written as:

acl setuser u on >pass +@write ~k1 ~k2 (+@root -del rootkeys ~k3)

The +@root can only be used inside a selector, and is also restricted at the beginning of a selector.

Another thing that needs to mention is that with these 2 keywords a selector can only inherit the permissions and key patterns from the root selector at the time the selector is parsed, later changes about the root selector do not affect the selectors.

So we can play some tricks for example:

acl setuser u on >pass +@all ~k1 (+@root rootkeys) -del ~k2 (+@root rootkeys) -@write ~k3 (+@root rootkeys) -@all resetkeys

This ACL rule does the following things:

  1. When parsing at acl setuser u on >pass +@all ~k1 (+@root rootkeys), we create a selector that gives all permissions on k1

  2. When we reach the second selector, acl setuser u on >pass +@all ~k1 (+@root rootkeys) -del ~k2 (+@root rootkeys), the root selector had changed, but the earlier selector stays the same, and, we create another new selector that gives all permissions except del on k1 and k2

  3. When we reach the last selector, we create a new selector that gives +@all -del -@write permissions on k1, k2, and k3.

  4. Finally, we use -@all resetkeys to clear and reset the root selector, and we are free to set the root selector to anything we want in the future without changing the 3 selectors we created

Without these new keywords, we can use separate ACL rules to achieve the same result, but that's far more complicated.

Alternatives you've considered

I read this redis doc and it seems there is no similar feature to achieve this right now

Additional information

Demo of this feature may looks like these:

Comment From: oranagra

so in essence, this is a syntactic sugar, to avoid some repetition, and doesn't add any capability.

on first glance, it seems nice, but in theory, for very complex ACLs we may even want to name selectors and use them in other selectors (not just use the root one). on the other hand, i suppose that in some complex systems, the ACL is actually either generated by software or edited by some GUI and this isn't needed.

so i wonder if introducing this capability now makes sense. @madolson WDYT?

Comment From: madolson

Well I have two minds.

The first is that I honestly think key based permissions were supposed to provide this type of simplification, and perhaps we actually want to add "Delete" permissions after all. This would allow you to simply write:

acl setuser u on >pass +@write ~k1 ~k2 %RIU~k3

Assuming I is insert and U is update. Key based selectors were supposed to be helping with these workloads where you have different permissions on keys, and you wanted to simplify the selectors you were writing. I can highlight this by saying that your proposed new rules have a major issue in that you are missing "unlink" in your second selector clause, so someone might accidentally delete it with unlink. You could also accidentally copy over it.

The second mind is that I think for the time being the selectors are already fairly complex, and I don't think we are at the point where syntactic sugar makes sense until we really understand how most people are using it. So for now, I would bias towards adding as little new syntax as possible.

Comment From: jerrykcode

Thanks for your responses!

@madolson You are right, I forgot to exclude unlink in the second selector.

I think it is good to use key permissions to simplify the writing, but this requires us to introduce permissions like %I(insert) %U(update), and may %D(delete). It seems that currently we only have %R and %W.

And even more, we may want to assign permissions for specific operations to certain key patterns. The key permission now can only be accurate to a class of operations, like read or write. Now we can't use key permission to restrict a key on a specific operation.

Comment From: madolson

@jerrykcode

And even more, we may want to assign permissions for specific operations to certain key patterns. The key permission now can only be accurate to a class of operations, like read or write. Now we can't use key permission to restrict a key on a specific operation.

The problem is this uncovers a deep rabbit whole from a security perspective. For example, you could also execute GETEX k3 0 which would also immediately delete the key. We are under the impression that if you have write access to a key, you can probably mess with it (delete it, corrupt it, etc). This led us to the implementation that we have, where we didn't really think users wanted to scope down a specific operation from a set of keys like you have. If you do, we really just grant read and write permissions.

Maybe you can elaborate a bit more on the use case you have where you need these special permissions for just k3 in your example. It would help inform the prioritization at the very least.

Comment From: jerrykcode

@madolson you are right, many other commands(not just del) can actually perform the deletion, so it's better not to split delete permission from the write category. So my use case should better be altered and now it can be satisfied by using a simple key permission(~%R).

The initial purpose of this issue is trying to solve this problem:

For example we have 2 selectors:

(~k1 +@read +permission1) (~k2 +@read +permission2)

permission1 and permission2 are just some permissions, they are different.

We can't execute this command mget k1 k2 even though both the 2 selectors have read permission.

If we want to execute that, we have to add a 3rd selector:

(~k1 ~k2 +@read)

Some commands are allowed by all selectors, but executing them with keys in different selectors is forbidden. To allow this we need to add a new selector which gives permission for that common command to all the keys. And this issue is hoping to simplify this by introducing keywords to inherit keys or permissions from the root selector(or even other selectors).

But now it seems that there are rare use cases that need this, so this issue may be meaningless.

Comment From: madolson

I never mind brining up potential features, even if we end up deciding they're not useful. I marked this as to be closed since we don't plan on implementing it, but maybe someone in the future will find another valid use case for this feature and we'll discuss it further then.