From 0e019cc08d0e20375e77179ea1c1e6b94d744665 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:57:12 +0200 Subject: [PATCH] Extended core support by implementing ACL SETUSER, GETUSER, DRYRUN (#1193) * Added support for ACL GETUSER, SETUSER, DRYRUN commands * Change test to support Redis > 6.0.0 * Removed selectors check --- examples/Commands/acl_dry_run.php | 36 ++++++ examples/Commands/acl_get_user.php | 28 +++++ examples/Commands/acl_set_user.php | 24 ++++ src/ClientContextInterface.php | 2 + src/ClientInterface.php | 2 + src/Command/Redis/ACL.php | 29 +++++ src/Command/Redis/Container/ACL.php | 28 +++++ tests/Predis/Command/Redis/ACL_Test.php | 149 ++++++++++++++++++++++++ 8 files changed, 298 insertions(+) create mode 100644 examples/Commands/acl_dry_run.php create mode 100644 examples/Commands/acl_get_user.php create mode 100644 examples/Commands/acl_set_user.php create mode 100644 src/Command/Redis/ACL.php create mode 100644 src/Command/Redis/Container/ACL.php create mode 100644 tests/Predis/Command/Redis/ACL_Test.php diff --git a/examples/Commands/acl_dry_run.php b/examples/Commands/acl_dry_run.php new file mode 100644 index 00000000..7cd82974 --- /dev/null +++ b/examples/Commands/acl_dry_run.php @@ -0,0 +1,36 @@ +acl->setUser('Test_dry', '+SET', '~*'); +$created = ($response == 'OK') ? 'Yes' : 'No'; + +echo "User with username 'Test' was created: {$created}. Permissions only to use SET command\n"; + +// 2. Dry run 'SET' command under 'Test_dry' user +$response = $client->acl->dryRun('Test_dry', 'SET', 'foo', 'bar'); + +echo 'Dry run "SET" command.' . "\n"; +echo 'Response: ' . $response . "\n"; + +// 3. Dry run 'GET' command under 'Test_dry' user +$response = $client->acl->dryRun('Test_dry', 'GET', 'foo'); + +echo 'Dry run "GET" command.' . "\n"; +echo 'Response: ' . $response; diff --git a/examples/Commands/acl_get_user.php b/examples/Commands/acl_get_user.php new file mode 100644 index 00000000..d6100748 --- /dev/null +++ b/examples/Commands/acl_get_user.php @@ -0,0 +1,28 @@ +acl->setUser('Test'); + +// 2. Retrieve user rules: + +echo 'Rules: ' . "\n"; +print_r( + $client->acl->getUser('Test') +); diff --git a/examples/Commands/acl_set_user.php b/examples/Commands/acl_set_user.php new file mode 100644 index 00000000..6fe19680 --- /dev/null +++ b/examples/Commands/acl_set_user.php @@ -0,0 +1,24 @@ +acl->setUser('Test'); +$created = ($response == 'OK') ? 'Yes' : 'No'; + +echo "User with username 'Test' was created: {$created}"; diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index c45700f4..e3581b30 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -17,6 +17,7 @@ use Predis\Command\Argument\Geospatial\FromInterface; use Predis\Command\Argument\Server\LimitOffsetCount; use Predis\Command\Argument\Server\To; use Predis\Command\CommandInterface; +use Predis\Command\Redis\Container\ACL; use Predis\Command\Redis\Container\FunctionContainer; /** @@ -206,6 +207,7 @@ use Predis\Command\Redis\Container\FunctionContainer; * * Container commands * @property FunctionContainer $function + * @property ACL $acl */ interface ClientContextInterface { diff --git a/src/ClientInterface.php b/src/ClientInterface.php index a084c990..b86952af 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -18,6 +18,7 @@ use Predis\Command\Argument\Server\LimitOffsetCount; use Predis\Command\Argument\Server\To; use Predis\Command\CommandInterface; use Predis\Command\FactoryInterface; +use Predis\Command\Redis\Container\ACL; use Predis\Command\Redis\Container\FunctionContainer; use Predis\Configuration\OptionsInterface; use Predis\Connection\ConnectionInterface; @@ -224,6 +225,7 @@ use Predis\Response\Status; * * Container commands * @property FunctionContainer $function + * @property ACL $acl */ interface ClientInterface { diff --git a/src/Command/Redis/ACL.php b/src/Command/Redis/ACL.php new file mode 100644 index 00000000..4cfbdb25 --- /dev/null +++ b/src/Command/Redis/ACL.php @@ -0,0 +1,29 @@ +getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testDryRunFilterArguments(): void + { + $arguments = ['DRYRUN', 'username', 'command', 'arg1', 'arg2']; + $expected = ['DRYRUN', 'username', 'command', 'arg1', 'arg2']; + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testGetUserFilterArguments(): void + { + $arguments = ['GETUSER', 'username']; + $expected = ['GETUSER', 'username']; + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSameValues($expected, $command->getArguments()); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 6.0.0 + */ + public function testSetUserCreatesACLUser(): void + { + $redis = $this->getClient(); + + $this->assertEquals('OK', $redis->acl->setUser('Test')); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 7.0.0 + */ + public function testDryRunSimulateExecutionOfGivenCommandByUser(): void + { + $redis = $this->getClient(); + + $this->assertEquals('OK', $redis->acl->setUser('Test', '+SET', '~*')); + $this->assertEquals( + 'OK', + $redis->acl->dryRun('Test', 'SET', 'foo', 'bar') + ); + $this->assertEquals( + "This user has no permissions to run the 'get' command", + $redis->acl->dryRun('Test', 'GET', 'foo') + ); + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 6.0.0 + */ + public function testGetUserReturnsUserDefinedRules(): void + { + $redis = $this->getClient(); + + $this->assertEquals( + 'OK', + $redis->acl->setUser( + 'alan', + 'allkeys', + '+@string', + '+@set', + '-SADD', + '>alanpassword' + ) + ); + + foreach (['flags', 'passwords', 'commands', 'keys', 'channels'] as $key) { + $this->assertContains($key, $redis->acl->getUser('alan')); + } + } + + /** + * @group connected + * @return void + * @requiresRedisVersion >= 6.0.0 + */ + public function testSetUserThrowsExceptionOnIncorrectRuleProvided(): void + { + $redis = $this->getClient(); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage("ERR Error in ACL SETUSER modifier 'foobar'"); + + $redis->acl->setUser('Test', 'foobar'); + } +}