Add readonly flag to EVAL_RO, EVALSHA_RO and FCALL_RO (#10728)
* Add readonly flag to EVAL_RO, EVALSHA_RO and FCALL_RO * Require users to explicitly declare @scripting to get access to lua scripting.
This commit is contained in:
parent
7a550c8bbb
commit
ed29d634b3
@ -7279,10 +7279,10 @@ struct redisCommand redisCommandTable[] = {
|
||||
/* scripting */
|
||||
{"eval","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_History,EVAL_tips,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_Args},
|
||||
{"evalsha","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_History,EVALSHA_tips,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_Args},
|
||||
{"evalsha_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,EVALSHA_RO_tips,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_RO_Args},
|
||||
{"eval_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_RO_History,EVAL_RO_tips,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_RO_Args},
|
||||
{"evalsha_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,EVALSHA_RO_tips,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_RO_Args},
|
||||
{"eval_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_RO_History,EVAL_RO_tips,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_RO_Args},
|
||||
{"fcall","Invoke a function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_History,FCALL_tips,fcallCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_Args},
|
||||
{"fcall_ro","Invoke a read-only function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_RO_History,FCALL_RO_tips,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_RO_Args},
|
||||
{"fcall_ro","Invoke a read-only function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_RO_History,FCALL_RO_tips,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_RO_Args},
|
||||
{"function","A container for function commands","Depends on subcommand.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_History,FUNCTION_tips,NULL,-2,0,0,.subcommands=FUNCTION_Subcommands},
|
||||
{"script","A container for Lua scripts management commands","Depends on subcommand.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_History,SCRIPT_tips,NULL,-2,0,0,.subcommands=SCRIPT_Subcommands},
|
||||
/* sentinel */
|
||||
|
@ -11,7 +11,8 @@
|
||||
"NOSCRIPT",
|
||||
"SKIP_MONITOR",
|
||||
"NO_MANDATORY_KEYS",
|
||||
"STALE"
|
||||
"STALE",
|
||||
"READONLY"
|
||||
],
|
||||
"acl_categories": [
|
||||
"SCRIPTING"
|
||||
|
@ -11,7 +11,8 @@
|
||||
"NOSCRIPT",
|
||||
"SKIP_MONITOR",
|
||||
"NO_MANDATORY_KEYS",
|
||||
"STALE"
|
||||
"STALE",
|
||||
"READONLY"
|
||||
],
|
||||
"acl_categories": [
|
||||
"SCRIPTING"
|
||||
|
@ -11,7 +11,8 @@
|
||||
"NOSCRIPT",
|
||||
"SKIP_MONITOR",
|
||||
"NO_MANDATORY_KEYS",
|
||||
"STALE"
|
||||
"STALE",
|
||||
"READONLY"
|
||||
],
|
||||
"acl_categories": [
|
||||
"SCRIPTING"
|
||||
|
11
src/server.c
11
src/server.c
@ -2716,7 +2716,8 @@ void commandAddSubcommand(struct redisCommand *parent, struct redisCommand *subc
|
||||
void setImplicitACLCategories(struct redisCommand *c) {
|
||||
if (c->flags & CMD_WRITE)
|
||||
c->acl_categories |= ACL_CATEGORY_WRITE;
|
||||
if (c->flags & CMD_READONLY)
|
||||
/* Exclude scripting commands from the RO category. */
|
||||
if (c->flags & CMD_READONLY && !(c->acl_categories & ACL_CATEGORY_SCRIPTING))
|
||||
c->acl_categories |= ACL_CATEGORY_READ;
|
||||
if (c->flags & CMD_ADMIN)
|
||||
c->acl_categories |= ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS;
|
||||
@ -3392,8 +3393,12 @@ void call(client *c, int flags) {
|
||||
(CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP);
|
||||
|
||||
/* If the client has keys tracking enabled for client side caching,
|
||||
* make sure to remember the keys it fetched via this command. */
|
||||
if (c->cmd->flags & CMD_READONLY) {
|
||||
* make sure to remember the keys it fetched via this command. Scripting
|
||||
* works a bit differently, where if the scripts executes any read command, it
|
||||
* remembers all of the declared keys from the script. */
|
||||
if ((c->cmd->flags & CMD_READONLY) && (c->cmd->proc != evalRoCommand)
|
||||
&& (c->cmd->proc != evalShaRoCommand) && (c->cmd->proc != fcallroCommand))
|
||||
{
|
||||
client *caller = (c->flags & CLIENT_SCRIPT && server.script_caller) ?
|
||||
server.script_caller : c;
|
||||
if (caller->flags & CLIENT_TRACKING &&
|
||||
|
@ -529,6 +529,13 @@ start_server {tags {"acl external:skip"}} {
|
||||
assert_equal [lsearch [r acl cat stream] "get"] -1
|
||||
}
|
||||
|
||||
test "ACL requires explicit permission for scripting for EVAL_RO, EVALSHA_RO and FCALL_RO" {
|
||||
r ACL SETUSER scripter on nopass +readonly
|
||||
assert_equal "This user has no permissions to run the 'eval_ro' command" [r ACL DRYRUN scripter EVAL_RO "" 0]
|
||||
assert_equal "This user has no permissions to run the 'evalsha_ro' command" [r ACL DRYRUN scripter EVALSHA_RO "" 0]
|
||||
assert_equal "This user has no permissions to run the 'fcall_ro' command" [r ACL DRYRUN scripter FCALL_RO "" 0]
|
||||
}
|
||||
|
||||
test {ACL #5998 regression: memory leaks adding / removing subcommands} {
|
||||
r AUTH default ""
|
||||
r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c
|
||||
|
@ -208,6 +208,33 @@ start_server {tags {"tracking network"}} {
|
||||
assert {$res eq {key1}}
|
||||
}
|
||||
|
||||
test {Tracking only occurs for scripts when a command calls a read-only command} {
|
||||
r CLIENT TRACKING off
|
||||
r CLIENT TRACKING on
|
||||
$rd_sg MSET key2{t} 1 key2{t} 1
|
||||
|
||||
# If a script doesn't call any read command, don't track any keys
|
||||
r EVAL "redis.call('set', 'key3{t}', 'bar')" 2 key1{t} key2{t}
|
||||
$rd_sg MSET key2{t} 2 key1{t} 2
|
||||
|
||||
# If a script calls a read command, track all declared keys
|
||||
r EVAL "redis.call('get', 'key3{t}')" 2 key1{t} key2{t}
|
||||
$rd_sg MSET key2{t} 2 key1{t} 2
|
||||
assert_equal {invalidate key2{t}} [r read]
|
||||
assert_equal {invalidate key1{t}} [r read]
|
||||
|
||||
# RO variants work like the normal variants
|
||||
r EVAL_RO "redis.call('ping')" 2 key1{t} key2{t}
|
||||
$rd_sg MSET key2{t} 2 key1{t} 2
|
||||
|
||||
r EVAL_RO "redis.call('get', 'key1{t}')" 2 key1{t} key2{t}
|
||||
$rd_sg MSET key2{t} 3 key1{t} 3
|
||||
assert_equal {invalidate key2{t}} [r read]
|
||||
assert_equal {invalidate key1{t}} [r read]
|
||||
|
||||
assert_equal "PONG" [r ping]
|
||||
}
|
||||
|
||||
test {RESP3 Client gets tracking-redir-broken push message after cached key changed when rediretion client is terminated} {
|
||||
r CLIENT TRACKING on REDIRECT $redir_id
|
||||
$rd_sg SET key1 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user