From 7f714ed0ba8e5a7432fa8c0991eadcfcef143831 Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Fri, 2 Sep 2022 21:59:15 +0100 Subject: [PATCH 1/6] [Messenger] Allow password in redis dsn when using sockets --- .../Redis/Tests/Transport/ConnectionTest.php | 55 +++++++++++++++++++ .../Bridge/Redis/Transport/Connection.php | 26 +++++++++ 2 files changed, 81 insertions(+) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php index 5089dec49f85c..b15d3a424322f 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php @@ -53,6 +53,61 @@ public function testFromDsnOnUnixSocket() ); } + public function testFromDsnOnUnixSocketWithUserAndPassword() + { + $redis = $this->createMock(\Redis::class); + + $redis->expects($this->exactly(1))->method('auth') + ->with(['user','password']) + ->willReturn(true); + + $this->assertEquals( + new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ + 'host' => '/var/run/redis/redis.sock', + 'port' => 0, + 'user'=> 'user', + 'pass'=> 'password' + ], [], $redis), + Connection::fromDsn('redis://user:password@/var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) + ); + } + + public function testFromDsnOnUnixSocketWithPassword() + { + $redis = $this->createMock(\Redis::class); + + $redis->expects($this->exactly(1))->method('auth') + ->with('password') + ->willReturn(true); + + $this->assertEquals( + new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ + 'host' => '/var/run/redis/redis.sock', + 'port' => 0, + 'pass'=>'password' + ], [], $redis), + Connection::fromDsn('redis://password@/var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) + ); + } + + public function testFromDsnOnUnixSocketWithUser() + { + $redis = $this->createMock(\Redis::class); + + $redis->expects($this->exactly(1))->method('auth') + ->with('user') + ->willReturn(true); + + $this->assertEquals( + new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ + 'host' => '/var/run/redis/redis.sock', + 'port' => 0, + 'user' => 'user', + ], [], $redis), + Connection::fromDsn('redis://user:@/var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) + ); + } + public function testFromDsnWithOptions() { $this->assertEquals( diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index 466d52fe24be1..6abfbe0ed892c 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -256,9 +256,14 @@ public static function fromDsn(string $dsn, array $redisOptions = [], $redis = n $connectionCredentials['host'] = 'tls://'.$connectionCredentials['host']; } } else { + $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; + $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; + $connectionCredentials = [ 'host' => $parsedUrl['path'], 'port' => 0, + // See: https://github.com/phpredis/phpredis/#auth + 'auth' => $redisOptions['auth'] ?? (null !== $pass && null !== $user ? [$user, $pass] : ($pass ?? $user)), ]; } @@ -274,9 +279,30 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $url = str_replace($scheme.':', 'file:', $dsn); } + $url = preg_replace_callback('#^' . $scheme . ':(//)?(?:(?:(?[^:@]*+):)?(?[^@]*+)@)?#', function ($m) use (&$auth) { + if (isset($m['password'])) { + if (!\in_array($m['user'], ['', 'default'], true)) { + $auth['user']=$m['user']; + } + + $auth['pass'] = $m['password']; + } + + return 'file:' . ($m[1] ?? ''); + }, $url); + if (false === $parsedUrl = parse_url($url)) { throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn)); } + + $parsedUrl = $parsedUrl + ($auth ?? []); + + if (array_key_exists('host', $parsedUrl)){ + $parsedUrl = ['scheme' => $scheme, 'host' => $parsedUrl['host'], 'port' => $parsedUrl['port'] ?? '6379'] + $parsedUrl; + } else { + $parsedUrl = ['scheme' => 'unix', 'path' => $parsedUrl['path']] + $parsedUrl; + } + if (isset($parsedUrl['query'])) { parse_str($parsedUrl['query'], $dsnOptions); $redisOptions = array_merge($redisOptions, $dsnOptions); From 88a7de270c5dc966fb8e6d14d549906dd03b9f2c Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Fri, 2 Sep 2022 22:30:50 +0100 Subject: [PATCH 2/6] Code Style --- .../Bridge/Redis/Tests/Transport/ConnectionTest.php | 8 ++++---- .../Messenger/Bridge/Redis/Transport/Connection.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php index b15d3a424322f..6ebe4c5af9636 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Tests/Transport/ConnectionTest.php @@ -58,15 +58,15 @@ public function testFromDsnOnUnixSocketWithUserAndPassword() $redis = $this->createMock(\Redis::class); $redis->expects($this->exactly(1))->method('auth') - ->with(['user','password']) + ->with(['user', 'password']) ->willReturn(true); $this->assertEquals( new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ 'host' => '/var/run/redis/redis.sock', 'port' => 0, - 'user'=> 'user', - 'pass'=> 'password' + 'user' => 'user', + 'pass' => 'password', ], [], $redis), Connection::fromDsn('redis://user:password@/var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) ); @@ -84,7 +84,7 @@ public function testFromDsnOnUnixSocketWithPassword() new Connection(['stream' => 'queue', 'delete_after_ack' => true], [ 'host' => '/var/run/redis/redis.sock', 'port' => 0, - 'pass'=>'password' + 'pass' => 'password', ], [], $redis), Connection::fromDsn('redis://password@/var/run/redis/redis.sock', ['stream' => 'queue', 'delete_after_ack' => true], $redis) ); diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index 6abfbe0ed892c..cd175c70468f5 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -279,16 +279,16 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $url = str_replace($scheme.':', 'file:', $dsn); } - $url = preg_replace_callback('#^' . $scheme . ':(//)?(?:(?:(?[^:@]*+):)?(?[^@]*+)@)?#', function ($m) use (&$auth) { + $url = preg_replace_callback('#^'.$scheme.':(//)?(?:(?:(?[^:@]*+):)?(?[^@]*+)@)?#', function ($m) use (&$auth) { if (isset($m['password'])) { if (!\in_array($m['user'], ['', 'default'], true)) { - $auth['user']=$m['user']; + $auth['user'] = $m['user']; } $auth['pass'] = $m['password']; } - return 'file:' . ($m[1] ?? ''); + return 'file:'.($m[1] ?? ''); }, $url); if (false === $parsedUrl = parse_url($url)) { @@ -297,7 +297,7 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $parsedUrl = $parsedUrl + ($auth ?? []); - if (array_key_exists('host', $parsedUrl)){ + if (\array_key_exists('host', $parsedUrl)) { $parsedUrl = ['scheme' => $scheme, 'host' => $parsedUrl['host'], 'port' => $parsedUrl['port'] ?? '6379'] + $parsedUrl; } else { $parsedUrl = ['scheme' => 'unix', 'path' => $parsedUrl['path']] + $parsedUrl; From ff29ec352e2d4c48439c814a76e9c3a65175b249 Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Fri, 2 Sep 2022 23:34:52 +0100 Subject: [PATCH 3/6] Improve --- .../Bridge/Redis/Transport/Connection.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index cd175c70468f5..569383b1b57ce 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -275,10 +275,12 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $url = $dsn; $scheme = 0 === strpos($dsn, 'rediss:') ? 'rediss' : 'redis'; + // if scheme:///... if (preg_match('#^'.$scheme.':///([^:@])+$#', $dsn)) { $url = str_replace($scheme.':', 'file:', $dsn); } + // if scheme://... $url = preg_replace_callback('#^'.$scheme.':(//)?(?:(?:(?[^:@]*+):)?(?[^@]*+)@)?#', function ($m) use (&$auth) { if (isset($m['password'])) { if (!\in_array($m['user'], ['', 'default'], true)) { @@ -287,7 +289,6 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $auth['pass'] = $m['password']; } - return 'file:'.($m[1] ?? ''); }, $url); @@ -295,12 +296,14 @@ private static function parseDsn(string $dsn, array &$redisOptions): array throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn)); } - $parsedUrl = $parsedUrl + ($auth ?? []); + if ($auth !== null) { + unset($parsedUrl['user']); // parse_url thinks //0@localhost/ is a password of "0"! doh! + $parsedUrl = $parsedUrl + ($auth ?? []); // But don't worry as $auth array will have user, user/pass or pass as needed + } - if (\array_key_exists('host', $parsedUrl)) { - $parsedUrl = ['scheme' => $scheme, 'host' => $parsedUrl['host'], 'port' => $parsedUrl['port'] ?? '6379'] + $parsedUrl; - } else { - $parsedUrl = ['scheme' => 'unix', 'path' => $parsedUrl['path']] + $parsedUrl; + // revert scheme now we are finished using PHP parse_url + if ($parsedUrl['scheme'] == 'file') { + $parsedUrl['scheme'] = $scheme; } if (isset($parsedUrl['query'])) { From ce978c3afc521a2268bf32f18ff3ff63f30322b9 Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Fri, 2 Sep 2022 23:36:17 +0100 Subject: [PATCH 4/6] Code Style --- .../Messenger/Bridge/Redis/Transport/Connection.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index 569383b1b57ce..dc17917997619 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -289,6 +289,7 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $auth['pass'] = $m['password']; } + return 'file:'.($m[1] ?? ''); }, $url); @@ -296,13 +297,13 @@ private static function parseDsn(string $dsn, array &$redisOptions): array throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn)); } - if ($auth !== null) { + if (null !== $auth) { unset($parsedUrl['user']); // parse_url thinks //0@localhost/ is a password of "0"! doh! $parsedUrl = $parsedUrl + ($auth ?? []); // But don't worry as $auth array will have user, user/pass or pass as needed } // revert scheme now we are finished using PHP parse_url - if ($parsedUrl['scheme'] == 'file') { + if ('file' == $parsedUrl['scheme']) { $parsedUrl['scheme'] = $scheme; } From 909eb330716fec51d92349863072078c989ab31c Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Sat, 3 Sep 2022 00:17:15 +0100 Subject: [PATCH 5/6] s/password/username --- .../Component/Messenger/Bridge/Redis/Transport/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index dc17917997619..e187a023bbabd 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -298,7 +298,7 @@ private static function parseDsn(string $dsn, array &$redisOptions): array } if (null !== $auth) { - unset($parsedUrl['user']); // parse_url thinks //0@localhost/ is a password of "0"! doh! + unset($parsedUrl['user']); // parse_url thinks //0@localhost/ is a username of "0"! doh! $parsedUrl = $parsedUrl + ($auth ?? []); // But don't worry as $auth array will have user, user/pass or pass as needed } From c569332b42c9a1ea4d7426e763a650ad7350f941 Mon Sep 17 00:00:00 2001 From: "Phil E. Taylor" Date: Thu, 12 Jan 2023 09:57:03 +0000 Subject: [PATCH 6/6] Code cleanup as requested --- .../Bridge/Redis/Transport/Connection.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index e187a023bbabd..e670a2d0f1063 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -237,9 +237,10 @@ public static function fromDsn(string $dsn, array $redisOptions = [], $redis = n 'claim_interval' => $claimInterval, ]; + $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; + $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; + if (isset($parsedUrl['host'])) { - $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; - $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; $connectionCredentials = [ 'host' => $parsedUrl['host'] ?? '127.0.0.1', 'port' => $parsedUrl['port'] ?? 6379, @@ -256,9 +257,6 @@ public static function fromDsn(string $dsn, array $redisOptions = [], $redis = n $connectionCredentials['host'] = 'tls://'.$connectionCredentials['host']; } } else { - $pass = '' !== ($parsedUrl['pass'] ?? '') ? $parsedUrl['pass'] : null; - $user = '' !== ($parsedUrl['user'] ?? '') ? $parsedUrl['user'] : null; - $connectionCredentials = [ 'host' => $parsedUrl['path'], 'port' => 0, @@ -275,12 +273,10 @@ private static function parseDsn(string $dsn, array &$redisOptions): array $url = $dsn; $scheme = 0 === strpos($dsn, 'rediss:') ? 'rediss' : 'redis'; - // if scheme:///... if (preg_match('#^'.$scheme.':///([^:@])+$#', $dsn)) { $url = str_replace($scheme.':', 'file:', $dsn); } - // if scheme://... $url = preg_replace_callback('#^'.$scheme.':(//)?(?:(?:(?[^:@]*+):)?(?[^@]*+)@)?#', function ($m) use (&$auth) { if (isset($m['password'])) { if (!\in_array($m['user'], ['', 'default'], true)) { @@ -299,11 +295,11 @@ private static function parseDsn(string $dsn, array &$redisOptions): array if (null !== $auth) { unset($parsedUrl['user']); // parse_url thinks //0@localhost/ is a username of "0"! doh! - $parsedUrl = $parsedUrl + ($auth ?? []); // But don't worry as $auth array will have user, user/pass or pass as needed + $parsedUrl += ($auth ?? []); // But don't worry as $auth array will have user, user/pass or pass as needed } // revert scheme now we are finished using PHP parse_url - if ('file' == $parsedUrl['scheme']) { + if ('file' === $parsedUrl['scheme']) { $parsedUrl['scheme'] = $scheme; }