summaryrefslogtreecommitdiff
path: root/tests/modules
diff options
context:
space:
mode:
authorShaya Potter <shaya@redislabs.com>2022-09-22 16:29:00 +0300
committerGitHub <noreply@github.com>2022-09-22 16:29:00 +0300
commit6e993a5dfa38327f25c5c8eea0f0e98ffd5d2c21 (patch)
tree7f48d653bd4694d4b93ed254613a805e423ef195 /tests/modules
parent6d21560190fd5b09ff849ad1777e868d5e78da5f (diff)
downloadredis-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/Makefile3
-rw-r--r--tests/modules/usercall.c129
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;
+}