diff options
author | Shaya Potter <shaya@redislabs.com> | 2022-09-22 16:29:00 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-22 16:29:00 +0300 |
commit | 6e993a5dfa38327f25c5c8eea0f0e98ffd5d2c21 (patch) | |
tree | 7f48d653bd4694d4b93ed254613a805e423ef195 /tests/modules | |
parent | 6d21560190fd5b09ff849ad1777e868d5e78da5f (diff) | |
download | redis-6e993a5dfa38327f25c5c8eea0f0e98ffd5d2c21.tar.gz |
Add RM_SetContextUser to support acl validation in RM_Call (and scripts) (#10966)
Adds a number of user management/ACL validaiton/command execution functions to improve a
Redis module's ability to enforce ACLs correctly and easily.
* RM_SetContextUser - sets a RedisModuleUser on the context, which RM_Call will use to both
validate ACLs (if requested and set) as well as assign to the client so that scripts executed via
RM_Call will have proper ACL validation.
* RM_SetModuleUserACLString - Enables one to pass an entire ACL string, not just a single OP
and have it applied to the user
* RM_GetModuleUserACLString - returns a stringified version of the user's ACL (same format as dump
and list). Contains an optimization to cache the stringified version until the underlying ACL is modified.
* Slightly re-purpose the "C" flag to RM_Call from just being about ACL check before calling the
command, to actually running the command with the right user, so that it also affects commands
inside EVAL scripts. see #11231
Diffstat (limited to 'tests/modules')
-rw-r--r-- | tests/modules/Makefile | 3 | ||||
-rw-r--r-- | tests/modules/usercall.c | 129 |
2 files changed, 131 insertions, 1 deletions
diff --git a/tests/modules/Makefile b/tests/modules/Makefile index ac4c3e27b..56069406b 100644 --- a/tests/modules/Makefile +++ b/tests/modules/Makefile @@ -58,7 +58,8 @@ TEST_MODULES = \ eventloop.so \ moduleconfigs.so \ moduleconfigstwo.so \ - publish.so + publish.so \ + usercall.so .PHONY: all diff --git a/tests/modules/usercall.c b/tests/modules/usercall.c new file mode 100644 index 000000000..9eb84ef39 --- /dev/null +++ b/tests/modules/usercall.c @@ -0,0 +1,129 @@ +#include "redismodule.h" + +RedisModuleUser *user = NULL; + +int call_without_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc < 2) { + return RedisModule_WrongArity(ctx); + } + + const char *cmd = RedisModule_StringPtrLen(argv[1], NULL); + + RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, "Ev", argv + 2, argc - 2); + if (!rep) { + RedisModule_ReplyWithError(ctx, "NULL reply returned"); + } else { + RedisModule_ReplyWithCallReply(ctx, rep); + RedisModule_FreeCallReply(rep); + } + return REDISMODULE_OK; +} + +int call_with_user_flag(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc < 3) { + return RedisModule_WrongArity(ctx); + } + + RedisModule_SetContextUser(ctx, user); + + /* Append Ev to the provided flags. */ + RedisModuleString *flags = RedisModule_CreateStringFromString(ctx, argv[1]); + RedisModule_StringAppendBuffer(ctx, flags, "Ev", 2); + + const char* flg = RedisModule_StringPtrLen(flags, NULL); + const char* cmd = RedisModule_StringPtrLen(argv[2], NULL); + + RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, flg, argv + 3, argc - 3); + if (!rep) { + RedisModule_ReplyWithError(ctx, "NULL reply returned"); + } else { + RedisModule_ReplyWithCallReply(ctx, rep); + RedisModule_FreeCallReply(rep); + } + RedisModule_FreeString(ctx, flags); + + return REDISMODULE_OK; +} + +int add_to_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 2) { + return RedisModule_WrongArity(ctx); + } + + size_t acl_len; + const char *acl = RedisModule_StringPtrLen(argv[1], &acl_len); + + RedisModuleString *error; + int ret = RedisModule_SetModuleUserACLString(ctx, user, acl, &error); + if (ret) { + size_t len; + const char * e = RedisModule_StringPtrLen(error, &len); + RedisModule_ReplyWithError(ctx, e); + return REDISMODULE_OK; + } + + RedisModule_ReplyWithSimpleString(ctx, "OK"); + + return REDISMODULE_OK; +} + +int get_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + + if (argc != 1) { + return RedisModule_WrongArity(ctx); + } + + RedisModule_Assert(user != NULL); + + RedisModuleString *acl = RedisModule_GetModuleUserACLString(user); + + RedisModule_ReplyWithString(ctx, acl); + + RedisModule_FreeString(NULL, acl); + + return REDISMODULE_OK; +} + +int reset_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + + if (argc != 1) { + return RedisModule_WrongArity(ctx); + } + + if (user != NULL) { + RedisModule_FreeModuleUser(user); + } + + user = RedisModule_CreateModuleUser("module_user"); + + RedisModule_ReplyWithSimpleString(ctx, "OK"); + + return REDISMODULE_OK; +} + +int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + + if (RedisModule_Init(ctx,"usercall",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx,"usercall.call_without_user", call_without_user,"write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx,"usercall.call_with_user_flag", call_with_user_flag,"write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "usercall.add_to_acl", add_to_acl, "write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx,"usercall.reset_user", reset_user,"write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx,"usercall.get_acl", get_acl,"write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + return REDISMODULE_OK; +} |