diff --git a/src/server/acl/acl_family.cc b/src/server/acl/acl_family.cc index e0859ebff26..afaf286b9d1 100644 --- a/src/server/acl/acl_family.cc +++ b/src/server/acl/acl_family.cc @@ -215,6 +215,10 @@ void AclFamily::DelUser(CmdArgList args, ConnectionContext* cntx) { cntx->SendOk(); } +void AclFamily::WhoAmI(CmdArgList args, ConnectionContext* cntx) { + cntx->SendSimpleString(absl::StrCat("User is ", cntx->authed_username)); +} + using CI = dfly::CommandId; using MemberFunc = void (AclFamily::*)(CmdArgList args, ConnectionContext* cntx); @@ -229,6 +233,7 @@ constexpr uint32_t kAcl = acl::CONNECTION; constexpr uint32_t kList = acl::ADMIN | acl::SLOW | acl::DANGEROUS; constexpr uint32_t kSetUser = acl::ADMIN | acl::SLOW | acl::DANGEROUS; constexpr uint32_t kDelUser = acl::ADMIN | acl::SLOW | acl::DANGEROUS; +constexpr uint32_t kWhoAmI = acl::SLOW; // We can't implement the ACL commands and its respective subcommands LIST, CAT, etc // the usual way, (that is, one command called ACL which then dispatches to the subcommand @@ -245,6 +250,8 @@ void AclFamily::Register(dfly::CommandRegistry* registry) { .HFUNC(SetUser); *registry << CI{"ACL DELUSER", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, 2, 0, 0, 0, acl::kDelUser} .HFUNC(DelUser); + *registry << CI{"ACL WHOAMI", CO::ADMIN | CO::NOSCRIPT | CO::LOADING, 1, 0, 0, 0, acl::kWhoAmI} + .HFUNC(WhoAmI); } #undef HFUNC diff --git a/src/server/acl/acl_family.h b/src/server/acl/acl_family.h index cc8b7493889..c1cde0db063 100644 --- a/src/server/acl/acl_family.h +++ b/src/server/acl/acl_family.h @@ -31,6 +31,7 @@ class AclFamily final { void List(CmdArgList args, ConnectionContext* cntx); void SetUser(CmdArgList args, ConnectionContext* cntx); void DelUser(CmdArgList args, ConnectionContext* cntx); + void WhoAmI(CmdArgList args, ConnectionContext* cntx); // Helper function that updates all open connections and their // respective ACL fields on all the available proactor threads diff --git a/tests/dragonfly/acl_family_test.py b/tests/dragonfly/acl_family_test.py index d0e15a95dda..46a1eaa539f 100644 --- a/tests/dragonfly/acl_family_test.py +++ b/tests/dragonfly/acl_family_test.py @@ -196,22 +196,20 @@ async def test_acl_categories_multi_exec_squash(df_local_factory): async def test_acl_deluser(df_server): client = aioredis.Redis(port=df_server.port) - try: - res = await client.execute_command("ACL DELUSER adi") - except redis.exceptions.ResponseError as e: - assert e.args[0] == "User adi does not exist" + with pytest.raises(redis.exceptions.ResponseError): + await client.execute_command("ACL DELUSER random") - res = await client.execute_command("ACL SETUSER adi ON >pass +@transaction +@string") + res = await client.execute_command("ACL SETUSER george ON >pass +@transaction +@string") assert res == b"OK" - res = await client.execute_command("AUTH adi pass") + res = await client.execute_command("AUTH george pass") assert res == b"OK" await client.execute_command("MULTI") await client.execute_command("SET key 44") admin_client = aioredis.Redis(port=df_server.port) - await admin_client.execute_command("ACL DELUSER adi") + await admin_client.execute_command("ACL DELUSER george") with pytest.raises(redis.exceptions.ConnectionError): await client.execute_command("EXEC") @@ -220,7 +218,7 @@ async def test_acl_deluser(df_server): script = """ -for i = 1, 10000 do +for i = 1, 100000 do redis.call('SET', 'key', i) redis.call('SET', 'key1', i) redis.call('SET', 'key2', i) @@ -244,7 +242,7 @@ async def test_acl_del_user_while_running_lua_script(df_server): for i in range(1, 4): res = await admin_client.get(f"key{i}") - assert res == b"10000" + assert res == b"100000" await admin_client.close() @@ -258,12 +256,28 @@ async def test_acl_with_long_running_script(df_server): await asyncio.gather( client.eval(script, 4, "key", "key1", "key2", "key3"), - admin_client.execute_command("ACL SETUSER -@string -@scripting"), + admin_client.execute_command("ACL SETUSER roman -@string -@scripting"), ) for i in range(1, 4): res = await admin_client.get(f"key{i}") - assert res == b"10000" + assert res == b"100000" await client.close() await admin_client.close() + + +@pytest.mark.asyncio +async def test_acl_whoami(async_client): + await async_client.execute_command("ACL SETUSER kostas >kk +@ALL ON") + + with pytest.raises(redis.exceptions.ResponseError): + await async_client.execute_command("ACL WHOAMI WHO") + + result = await async_client.execute_command("ACL WHOAMI") + assert result == "User is default" + + result = await async_client.execute_command("AUTH kostas kk") + + result = await async_client.execute_command("ACL WHOAMI") + assert result == "User is kostas"