diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1eaccff..d2b3966 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: extensions: redis - name: Setup Redis - run: sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version + run: sudo add-apt-repository ppa:redislabs/redis -y -u ; sudo apt install redis -y ; sudo systemctl stop redis\* ; redis-cli --version - name: Install phpunit env: diff --git a/Client.php b/Client.php index 72b209a..4a253da 100755 --- a/Client.php +++ b/Client.php @@ -273,6 +273,11 @@ class Credis_Client { */ protected $isWatching = FALSE; + /** + * @var string + */ + protected $authUsername; + /** * @var string */ @@ -315,8 +320,9 @@ class Credis_Client { * @param string $persistent Flag to establish persistent connection * @param int $db The selected datbase of the Redis server * @param string $password The authentication password of the Redis server + * @param string $username The authentication username of the Redis server */ - public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null) + public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '', $db = 0, $password = null, $username = null) { $this->host = (string) $host; $this->port = (int) $port; @@ -325,10 +331,15 @@ public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $this->persistent = (string) $persistent; $this->standalone = ! extension_loaded('redis'); $this->authPassword = $password; + $this->authUsername = $username; $this->selectedDb = (int)$db; $this->convertHost(); - // PHP Redis extension support TLS since 5.3.0 - if ($this->scheme == 'tls' && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ + // PHP Redis extension support TLS/ACL AUTH since 5.3.0 + if (( + $this->scheme === 'tls' + || $this->authUsername !== null + ) + && !$this->standalone && version_compare(phpversion('redis'),'5.3.0','<')){ $this->standalone = true; } } @@ -504,9 +515,8 @@ public function connect() if ($this->readTimeout) { $this->setReadTimeout($this->readTimeout); } - if($this->authPassword) { - $this->auth($this->authPassword); + $this->auth($this->authPassword, $this->authUsername); } if($this->selectedDb !== 0) { $this->select($this->selectedDb); @@ -641,11 +651,17 @@ public function getRenamedCommand($command) /** * @param string $password + * @param string|null $username * @return bool */ - public function auth($password) + public function auth($password, $username = null) { - $response = $this->__call('auth', array($password)); + if ($username !== null) { + $response = $this->__call('auth', array($username, $password)); + $this->authUsername= $username; + } else { + $response = $this->__call('auth', array($password)); + } $this->authPassword = $password; return $response; } @@ -1150,6 +1166,10 @@ public function __call($name, $args) // allow phpredis to see the caller's reference //$param_ref =& $args[0]; break; + case 'auth': + // For phpredis pre-v5.3, the type signature is string, not array|string + $args = (is_array($args) && count($args) === 1) ? $args : array($args); + break; default: // Flatten arguments $args = self::_flattenArguments($args); @@ -1185,6 +1205,7 @@ public function __call($name, $args) return $this; } + // Send request, retry one time when using persistent connections on the first request only $this->requests++; try { diff --git a/tests/CredisTest.php b/tests/CredisTest.php index 3e1f392..6c0f38a 100644 --- a/tests/CredisTest.php +++ b/tests/CredisTest.php @@ -620,6 +620,73 @@ public function testPassword() $this->assertTrue($this->credis->set('key','value')); } + /** + * @group Auth + */ + public function testUsernameAndPassword() + { + $this->tearDown(); + $this->assertArrayHasKey('password',$this->redisConfig[7]); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, $this->redisConfig[7]['password'], $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + $this->assertInstanceOf('Credis_Client',$this->credis->connect()); + $this->assertTrue($this->credis->set('key','value')); + $this->credis->close(); + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0, 'wrongpassword', $this->redisConfig[7]['username']); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try + { + $this->credis->connect(); + $this->fail('connect should fail with wrong password'); + } + catch(CredisException $e) + { + if (strpos($e->getMessage(), 'username') !== false) + { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + + $this->credis->close(); + } + $this->credis = new Credis_Client($this->redisConfig[7]['host'], $this->redisConfig[7]['port'], $this->redisConfig[7]['timeout'], false, 0); + if ($this->useStandalone) { + $this->credis->forceStandalone(); + } + try + { + $this->credis->set('key', 'value'); + } + catch(CredisException $e) + { + $this->assertStringStartsWith('NOAUTH Authentication required', $e->getMessage()); + } + try + { + $this->credis->auth('anotherwrongpassword'); + } + catch(CredisException $e) + { + if (strpos($e->getMessage(), 'username') !== false) + { + $this->assertStringStartsWith('WRONGPASS invalid username-password pair', $e->getMessage()); + } + else + { + $this->assertStringStartsWith('ERR invalid password', $e->getMessage()); + } + } + $this->assertTrue($this->credis->auth('thepassword')); + $this->assertTrue($this->credis->set('key','value')); + } + public function testGettersAndSetters() { $this->assertEquals($this->credis->getHost(),$this->redisConfig[0]['host']); diff --git a/tests/redis_config.json b/tests/redis_config.json index c696fb3..f153f34 100644 --- a/tests/redis_config.json +++ b/tests/redis_config.json @@ -5,5 +5,6 @@ {"host":"127.0.0.1","port":6382,"timeout":2.5,"alias":"fourth"}, {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"auth", "password": "thepassword"}, {"host":"127.0.0.1","port":6384,"timeout":2.5,"alias":"socket"}, - {"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"} + {"host":"127.0.0.1","port":6385,"timeout":2.5,"alias":"slave"}, + {"host":"127.0.0.1","port":6383,"timeout":2.5,"alias":"userauth", "username": "default", "password": "thepassword"} ] \ No newline at end of file