Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d14bb1
Add database-level access control
dvkashapov Jul 4, 2025
97a27c5
Fix selector flag assignment and apply clang-format
dvkashapov Jul 7, 2025
b2fd61a
Extend serverCommand with get_dbid_args, unify ACL checks
dvkashapov Jul 14, 2025
b4adb23
Run generate commands
dvkashapov Oct 30, 2025
c0709a1
Merge remote-tracking branch 'upstream/unstable' into unstable
dvkashapov Oct 30, 2025
88d96ba
Fix tests and apply clang
dvkashapov Oct 30, 2025
98f4550
Delete CROSS_DB and NOT_IMPLEMENTED
dvkashapov Oct 30, 2025
e6c10dd
Delete CROSS_DB from commands.def
dvkashapov Oct 30, 2025
74ccbec
Add db+= and db-= syntax
dvkashapov Oct 31, 2025
8bc8d13
Fix comment style
dvkashapov Oct 31, 2025
a4397dc
clang-format fix
dvkashapov Oct 31, 2025
fa90b04
Refactor and add more tests
dvkashapov Nov 5, 2025
a3af4f6
Merge remote-tracking branch 'upstream/unstable' into unstable
dvkashapov Nov 6, 2025
15068cf
Fix reason in ACL LOG and add test
dvkashapov Nov 16, 2025
ec93e49
Use cmd->fullname when no argpos
dvkashapov Nov 16, 2025
711b4f3
Add module api for db-level check and test
dvkashapov Nov 16, 2025
fc5bc3a
apply pr suggestions
dvkashapov Nov 17, 2025
10d7499
apply clang-format
dvkashapov Nov 17, 2025
7f0e020
add ALL_DBS flag to migration/slot commands
dvkashapov Nov 17, 2025
b6ab15b
db= syntax, acl getuser support
dvkashapov Nov 26, 2025
0562230
Return ACL_DENIED_DB only for R/W commands in forbidden db
dvkashapov Nov 26, 2025
04568af
Use sds
dvkashapov Nov 26, 2025
19186e7
Merge remote-tracking branch 'upstream/unstable' into unstable
dvkashapov Nov 27, 2025
d35a3a7
Merge remote-tracking branch 'upstream/unstable' into unstable
dvkashapov Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 226 additions & 15 deletions src/acl.c

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/cli_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

/* Definitions to configure commands.c to generate the above structs. */
#define MAKE_CMD(name, summary, complexity, since, doc_flags, replaced, deprecated, group, group_enum, history, \
num_history, tips, num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, \
numargs) \
num_history, tips, num_tips, function, arity, flags, acl, get_dbid_args, key_specs, \
key_specs_num, get_keys, numargs) \
name, summary, group, since, numargs
#define MAKE_ARG(name, type, key_spec_index, token, summary, since, flags, numsubargs, deprecated_since) \
name, type, token, since, flags, numsubargs
Expand Down
6 changes: 3 additions & 3 deletions src/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#include "server.h"

#define MAKE_CMD(name, summary, complexity, since, doc_flags, replaced, deprecated, group, group_enum, history, \
num_history, tips, num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, \
numargs) \
num_history, tips, num_tips, function, arity, flags, acl, get_dbid_args, key_specs, \
key_specs_num, get_keys, numargs) \
name, summary, complexity, since, doc_flags, replaced, deprecated, group_enum, history, num_history, tips, \
num_tips, function, arity, flags, acl, key_specs, key_specs_num, get_keys, numargs
num_tips, function, arity, flags, acl, get_dbid_args, key_specs, key_specs_num, get_keys, numargs
#define MAKE_ARG(name, type, key_spec_index, token, summary, since, flags, numsubargs, deprecated_since) \
name, type, key_spec_index, token, summary, since, flags, deprecated_since, numsubargs
#define COMMAND_STRUCT serverCommand
Expand Down
846 changes: 423 additions & 423 deletions src/commands.def

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/commands/cluster-cancelslotmigrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"command_flags": [
"NO_ASYNC_LOADING",
"ADMIN",
"STALE"
"STALE",
"ALL_DBS"
],
"reply_schema": {
"const": "OK"
Expand Down
3 changes: 2 additions & 1 deletion src/commands/cluster-countkeysinslot.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"container": "CLUSTER",
"function": "clusterCommand",
"command_flags": [
"STALE"
"STALE",
"ALL_DBS"
],
"arguments": [
{
Expand Down
3 changes: 2 additions & 1 deletion src/commands/cluster-getkeysinslot.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"container": "CLUSTER",
"function": "clusterCommand",
"command_flags": [
"STALE"
"STALE",
"ALL_DBS"
],
"command_tips": [
"NONDETERMINISTIC_OUTPUT"
Expand Down
3 changes: 2 additions & 1 deletion src/commands/cluster-migrateslots.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"command_flags": [
"NO_ASYNC_LOADING",
"ADMIN",
"STALE"
"STALE",
"ALL_DBS"
],
"arguments": [
{
Expand Down
3 changes: 2 additions & 1 deletion src/commands/flushall.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
]
],
"command_flags": [
"WRITE"
"WRITE",
"ALL_DBS"
],
"acl_categories": [
"KEYSPACE",
Expand Down
1 change: 1 addition & 0 deletions src/commands/migrate.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"KEYSPACE",
"DANGEROUS"
],
"get_dbid_args": "migrateDbIdArgs",
"command_tips": [
"NONDETERMINISTIC_OUTPUT"
],
Expand Down
1 change: 1 addition & 0 deletions src/commands/move.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"acl_categories": [
"KEYSPACE"
],
"get_dbid_args": "moveDbIdArgs",
"key_specs": [
{
"flags": [
Expand Down
1 change: 1 addition & 0 deletions src/commands/select.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"acl_categories": [
"CONNECTION"
],
"get_dbid_args": "selectDbIdArgs",
"reply_schema": {
"const": "OK"
},
Expand Down
1 change: 1 addition & 0 deletions src/commands/swapdb.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"KEYSPACE",
"DANGEROUS"
],
"get_dbid_args": "swapdbDbIdArgs",
"arguments": [
{
"name": "index1",
Expand Down
59 changes: 59 additions & 0 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,10 @@ void selectCommand(client *c) {

if (getIntFromObjectOrReply(c, c->argv[1], &id, NULL) != C_OK) return;

if (c->flag.multi) {
c->mstate->transaction_db_id = id;
}

if (selectDb(c, id) == C_ERR) {
addReplyError(c, "DB index is out of range");
} else {
Expand Down Expand Up @@ -2961,3 +2965,58 @@ int bitfieldGetKeys(struct serverCommand *cmd, robj **argv, int argc, getKeysRes
}
return 1;
}


int *selectDbIdArgs(robj **argv, int argc, int *count) {
if (argc < 2) return NULL;

long long dbid;
if (getLongLongFromObject(argv[1], &dbid) != C_OK) return NULL;
if (dbid < 0 || dbid >= server.dbnum) return NULL;

int *result = zmalloc(sizeof(int));
result[0] = (int)dbid;
*count = 1;
return result;
}

int *swapdbDbIdArgs(robj **argv, int argc, int *count) {
if (argc < 3) return NULL;

long long db1, db2;
if (getLongLongFromObject(argv[1], &db1) != C_OK ||
getLongLongFromObject(argv[2], &db2) != C_OK) return NULL;
if (db1 < 0 || db1 >= server.dbnum || db2 < 0 || db2 >= server.dbnum) return NULL;

int *result = zmalloc(2 * sizeof(int));
result[0] = (int)db1;
result[1] = (int)db2;
*count = 2;
return result;
}

int *moveDbIdArgs(robj **argv, int argc, int *count) {
if (argc < 3) return NULL;

long long dbid;
if (getLongLongFromObject(argv[2], &dbid) != C_OK) return NULL;
if (dbid < 0 || dbid >= server.dbnum) return NULL;

int *result = zmalloc(sizeof(int));
result[0] = (int)dbid;
*count = 1;
return result;
}

int *migrateDbIdArgs(robj **argv, int argc, int *count) {
if (argc < 5) return NULL;

long long dbid;
if (getLongLongFromObject(argv[4], &dbid) != C_OK) return NULL;
if (dbid < 0 || dbid >= server.dbnum) return NULL;

int *result = zmalloc(sizeof(int));
result[0] = (int)dbid;
*count = 1;
return result;
}
15 changes: 15 additions & 0 deletions src/intset.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,18 @@ int intsetValidateIntegrity(const unsigned char *p, size_t size, int deep) {

return 1;
}

/* Free an intset */
void intsetFree(intset *is) {
if (is) zfree(is);
}

/* Duplicate an intset */
intset *intsetDup(intset *is) {
if (!is) return intsetNew();

size_t size = intsetBlobLen(is);
intset *copy = zmalloc(size);
memcpy(copy, is, size);
return copy;
}
2 changes: 2 additions & 0 deletions src/intset.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);
uint32_t intsetLen(const intset *is);
size_t intsetBlobLen(intset *is);
int intsetValidateIntegrity(const unsigned char *is, size_t size, int deep);
void intsetFree(intset *is);
intset *intsetDup(intset *is);

#endif // __INTSET_H
3 changes: 2 additions & 1 deletion src/lua/script_lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,8 @@ static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) {
raise_error = 1;
} else {
int keyidxptr;
if (ACLCheckAllUserCommandPerm(rctx->original_client->user, cmd, argv, argc, &keyidxptr) != ACL_OK) {
int dbid = (rctx->original_client->flag.multi) ? rctx->original_client->mstate->transaction_db_id : (rctx->original_client->db ? rctx->original_client->db->id : -1);
if (ACLCheckAllUserCommandPerm(rctx->original_client->user, cmd, argv, argc, dbid, &keyidxptr) != ACL_OK) {
lua_pushboolean(lua, 0);
} else {
lua_pushboolean(lua, 1);
Expand Down
31 changes: 28 additions & 3 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -6574,7 +6574,8 @@ ValkeyModuleCallReply *VM_Call(ValkeyModuleCtx *ctx, const char *cmdname, const
int acl_errpos;
int acl_retval;

acl_retval = ACLCheckAllUserCommandPerm(user, c->cmd, c->argv, c->argc, &acl_errpos);
int dbid = (c->flag.multi) ? c->mstate->transaction_db_id : (c->db ? c->db->id : -1);
acl_retval = ACLCheckAllUserCommandPerm(user, c->cmd, c->argv, c->argc, dbid, &acl_errpos);
if (acl_retval != ACL_OK) {
int context = scriptIsRunning() ? ACL_LOG_CTX_SCRIPT : ACL_LOG_CTX_MODULE;
sds object = (acl_retval == ACL_DENIED_CMD) ? sdsdup(c->cmd->fullname) : sdsdup(c->argv[acl_errpos]->ptr);
Expand Down Expand Up @@ -10075,7 +10076,7 @@ ValkeyModuleUser *VM_GetModuleUserFromUserName(ValkeyModuleString *name) {
* * ENOENT: Specified command does not exist.
* * EACCES: Command cannot be executed, according to ACL rules
*/
int VM_ACLCheckCommandPermissions(ValkeyModuleUser *user, ValkeyModuleString **argv, int argc) {
int VM_ACLCheckCommandPermissions(ValkeyModuleUser *user, ValkeyModuleString **argv, int argc, int dbid) {
int keyidxptr;
struct serverCommand *cmd;

Expand All @@ -10085,7 +10086,7 @@ int VM_ACLCheckCommandPermissions(ValkeyModuleUser *user, ValkeyModuleString **a
return VALKEYMODULE_ERR;
}

if (ACLCheckAllUserCommandPerm(user->user, cmd, argv, argc, &keyidxptr) != ACL_OK) {
if (ACLCheckAllUserCommandPerm(user->user, cmd, argv, argc, dbid, &keyidxptr) != ACL_OK) {
errno = EACCES;
return VALKEYMODULE_ERR;
}
Expand Down Expand Up @@ -10156,6 +10157,28 @@ int VM_ACLCheckChannelPermissions(ValkeyModuleUser *user, ValkeyModuleString *ch
return VALKEYMODULE_OK;
}

/* Check if the database can be accessed by the user.
*
* If the user is able to access the database then VALKEYMODULE_OK is returned, otherwise
* VALKEYMODULE_ERR is returned and errno is set to one of the following values:
*
* * EINVAL: The provided dbid is invalid (negative or >= server.dbnum).
* * EACCES: The user does not have permission to access the database.
*/
int VM_ACLCheckDbPermissions(ValkeyModuleUser *user, int dbid) {
if (dbid < 0 || dbid >= server.dbnum) {
errno = EINVAL;
return VALKEYMODULE_ERR;
}

if (ACLUserCheckDbPerm(user->user, dbid) != ACL_OK) {
errno = EACCES;
return VALKEYMODULE_ERR;
}

return VALKEYMODULE_OK;
}

/* Helper function to map a ValkeyModuleACLLogEntryReason to ACL Log entry reason. */
int moduleGetACLLogEntryReason(ValkeyModuleACLLogEntryReason reason) {
int acl_reason = 0;
Expand All @@ -10164,6 +10187,7 @@ int moduleGetACLLogEntryReason(ValkeyModuleACLLogEntryReason reason) {
case VALKEYMODULE_ACL_LOG_KEY: acl_reason = ACL_DENIED_KEY; break;
case VALKEYMODULE_ACL_LOG_CHANNEL: acl_reason = ACL_DENIED_CHANNEL; break;
case VALKEYMODULE_ACL_LOG_CMD: acl_reason = ACL_DENIED_CMD; break;
case VALKEYMODULE_ACL_LOG_DB: acl_reason = ACL_DENIED_DB; break;
default: break;
}
return acl_reason;
Expand Down Expand Up @@ -14527,6 +14551,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ACLCheckCommandPermissions);
REGISTER_API(ACLCheckKeyPermissions);
REGISTER_API(ACLCheckChannelPermissions);
REGISTER_API(ACLCheckDbPermissions);
REGISTER_API(ACLAddLogEntry);
REGISTER_API(ACLAddLogEntryByUserName);
REGISTER_API(FreeModuleUser);
Expand Down
12 changes: 12 additions & 0 deletions src/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ void resetClientMultiState(client *c) {
c->mstate->cmd_inv_flags = 0;
c->mstate->argv_len_sums = 0;
c->mstate->alloc_count = 0;
c->mstate->transaction_db_id = c->db->id;
}

/* Add a new command into the MULTI commands queue */
Expand Down Expand Up @@ -98,6 +99,15 @@ void queueMultiCommand(client *c, uint64_t cmd_flags) {
mc->argv_len = c->argv_len;
mc->slot = c->slot;

if (mc->cmd->proc == selectCommand && mc->argc > 1) {
long long target_db;
if (getLongLongFromObject(mc->argv[1], &target_db) == C_OK) {
if (target_db >= 0 && target_db < server.dbnum) {
c->mstate->transaction_db_id = (int)target_db;
}
}
}

c->mstate->count++;
c->mstate->cmd_flags |= cmd_flags;
c->mstate->cmd_inv_flags |= ~cmd_flags;
Expand Down Expand Up @@ -131,6 +141,7 @@ void flagTransaction(client *c) {
void multiCommand(client *c) {
if (!c->mstate) initClientMultiState(c);
c->flag.multi = 1;
c->mstate->transaction_db_id = c->db->id;
addReply(c, shared.ok);
}

Expand Down Expand Up @@ -207,6 +218,7 @@ void execCommand(client *c) {
orig_argv_len = c->argv_len;
orig_argc = c->argc;
orig_cmd = c->cmd;
c->mstate->transaction_db_id = c->db->id;
addReplyArrayLen(c, c->mstate->count);
for (j = 0; j < c->mstate->count; j++) {
c->argc = c->mstate->commands[j].argc;
Expand Down
1 change: 1 addition & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -2974,6 +2974,7 @@ void initServer(void) {
server.acl_info.user_auth_failures = 0;
server.acl_info.invalid_channel_accesses = 0;
server.acl_info.acl_access_denied_tls_cert = 0;
server.acl_info.invalid_db_accesses = 0;

/* Create the timer callback, this is our way to process many background
* operations incrementally, like eviction of unaccessed expired keys, etc. */
Expand Down
Loading
Loading