mirror of
https://github.com/predis/predis.git
synced 2026-09-04 14:56:47 +00:00
Added testing with SSL connection (#1624)
* Added testing with SSL connection * Update CHANGELOG.md * Revert changes * Codestyle fixes * Add version restriction * Fixed issue with connecting to SSL port to check redis version * Mark tests as relay-incompatible * Codestyle fixes
This commit is contained in:
committed by
GitHub
parent
0703c3cae6
commit
95e802430d
@@ -71,6 +71,7 @@ services:
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
|
||||
ports:
|
||||
- "6372-6377:6372-6377"
|
||||
- "27379-27384:27379-27384"
|
||||
volumes:
|
||||
- "./dockers/cluster:/redis/work"
|
||||
profiles:
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
- Added retry support (#1616)
|
||||
- Added support for VRANGE command (#1623)
|
||||
|
||||
### Maintenance
|
||||
- Added testing with SSL connection (#1624)
|
||||
|
||||
## v3.3.0 (2025-11-24)
|
||||
### Added
|
||||
- Added cluster support for `XADD`, `XDEL` and `XRANGE` (#1587)
|
||||
|
||||
@@ -55,6 +55,15 @@
|
||||
<env name="USE_RELAY" value="false" />
|
||||
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
|
||||
<!-- SSL -->
|
||||
<env name="STANDALONE_CA_CERT_PATH" value=".github/dockers/standalone/tls/ca.crt" />
|
||||
<env name="REDIS_SSL_PORT" value="6666" />
|
||||
<env name="CLUSTER_CA_CERT_PATH" value=".github/dockers/cluster/tls/ca.crt" />
|
||||
<const
|
||||
name="SSL_REDIS_CLUSTER_ENDPOINTS"
|
||||
value="127.0.0.1:27379?password=foobar,127.0.0.1:27380?password=foobar,127.0.0.1:27381?password=foobar"
|
||||
/>
|
||||
|
||||
<!-- Redis Cluster -->
|
||||
<!-- Only master nodes endpoints included -->
|
||||
<const
|
||||
|
||||
@@ -180,12 +180,14 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
} elseif ($this->isUnprotectedTest()) {
|
||||
$port = constant('REDIS_UNPROTECTED_SERVER_PORT');
|
||||
$password = '';
|
||||
} elseif ($this->isSSLTest()) {
|
||||
$port = getenv('REDIS_SSL_PORT');
|
||||
} else {
|
||||
$port = constant('REDIS_SERVER_PORT');
|
||||
}
|
||||
|
||||
return [
|
||||
'scheme' => 'tcp',
|
||||
'scheme' => $this->isSSLTest() ? 'tls' : 'tcp',
|
||||
'host' => constant('REDIS_SERVER_HOST'),
|
||||
'port' => $port,
|
||||
'database' => constant('REDIS_SERVER_DBNUM'),
|
||||
@@ -282,6 +284,28 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
],
|
||||
$options
|
||||
);
|
||||
|
||||
if ($this->isSSLTest()) {
|
||||
$options = array_merge($options, [
|
||||
'parameters' => [
|
||||
'ssl' => [
|
||||
'cafile' => getenv('CLUSTER_CA_CERT_PATH'),
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($this->isSSLTest()) {
|
||||
$parameters = array_merge($parameters, [
|
||||
'ssl' => [
|
||||
'cafile' => getenv('STANDALONE_CA_CERT_PATH'),
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$client = new Client($parameters, $options);
|
||||
@@ -294,6 +318,54 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client for version checking without SSL configuration.
|
||||
* This is used to check Redis version before attempting SSL connection.
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
protected function createClientForVersionCheck(): Client
|
||||
{
|
||||
// For SSL tests, temporarily override to use non-SSL configuration
|
||||
$isSSL = $this->isSSLTest();
|
||||
$isCluster = $this->isClusterTest();
|
||||
|
||||
if ($isSSL && $isCluster) {
|
||||
// For cluster SSL tests, use non-SSL cluster endpoints
|
||||
$endpoints = explode(',', constant('REDIS_CLUSTER_ENDPOINTS'));
|
||||
$parameters = array_map(static function (string $elem) {
|
||||
return 'tcp://' . $elem;
|
||||
}, $endpoints);
|
||||
} elseif ($isSSL) {
|
||||
// For standalone SSL tests, use non-SSL port
|
||||
$parameters = [
|
||||
'scheme' => 'tcp',
|
||||
'host' => constant('REDIS_SERVER_HOST'),
|
||||
'port' => constant('REDIS_SERVER_PORT'),
|
||||
'database' => constant('REDIS_SERVER_DBNUM'),
|
||||
'password' => getenv('REDIS_PASSWORD') ?: constant('REDIS_PASSWORD'),
|
||||
];
|
||||
} else {
|
||||
// For non-SSL tests, use default parameters
|
||||
$parameters = $this->getDefaultParametersArray();
|
||||
}
|
||||
|
||||
$commandsFactory = $this->getCommandFactory();
|
||||
$options = array_merge(
|
||||
['commands' => $commandsFactory],
|
||||
getenv('USE_RELAY') ? ['connections' => 'relay'] : []
|
||||
);
|
||||
|
||||
if ($isCluster) {
|
||||
$options['cluster'] = 'redis';
|
||||
}
|
||||
|
||||
$client = new Client($parameters, $options);
|
||||
$client->connect();
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a basic mock object of a connection to a single Redis node.
|
||||
*
|
||||
@@ -371,7 +443,9 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
if (isset($this->info)) {
|
||||
$info = $this->info;
|
||||
} else {
|
||||
$client = $this->createClient(null, null, true);
|
||||
// For SSL tests, connect to non-SSL port to check version first
|
||||
// This prevents connection failures on Redis < 7.2.0 which doesn't support SSL
|
||||
$client = $this->createClientForVersionCheck();
|
||||
$info = array_change_key_case($client->info());
|
||||
$this->info = $info;
|
||||
}
|
||||
@@ -383,7 +457,7 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
// Redis < 2.6
|
||||
$version = $info['redis_version'];
|
||||
} else {
|
||||
$client = $this->createClient(null, null, true);
|
||||
$client = $this->createClientForVersionCheck();
|
||||
$connection = $client->getConnection();
|
||||
throw new RuntimeException("Unable to retrieve a valid server info payload from $connection");
|
||||
}
|
||||
@@ -607,6 +681,34 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
&& in_array('cluster', $annotations['method']['group'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check annotations if it's matches to SSL test scenario.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSSLTest(): bool
|
||||
{
|
||||
$annotations = TestUtil::parseTestMethodAnnotations(
|
||||
get_class($this),
|
||||
$this->getName(false)
|
||||
);
|
||||
|
||||
$annotationExists = isset($annotations['method']['requiresRedisVersion']);
|
||||
|
||||
if (!$annotationExists) {
|
||||
foreach ($this->modulesMapping as $module => $configuration) {
|
||||
if (isset($annotations['method'][$configuration['annotation']])) {
|
||||
$annotationExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $annotationExists
|
||||
&& isset($annotations['method']['group'])
|
||||
&& in_array('connected', $annotations['method']['group'], true)
|
||||
&& in_array('ssl', $annotations['method']['group'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check annotations if it's matches to stack test scenario.
|
||||
*
|
||||
@@ -646,10 +748,14 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
*/
|
||||
protected function prepareClusterEndpoints(): array
|
||||
{
|
||||
$endpoints = explode(',', constant('REDIS_CLUSTER_ENDPOINTS'));
|
||||
$endpoints = explode(
|
||||
',',
|
||||
constant($this->isSSLTest() ? 'SSL_REDIS_CLUSTER_ENDPOINTS' : 'REDIS_CLUSTER_ENDPOINTS')
|
||||
);
|
||||
$scheme = $this->isSSLTest() ? 'tls' : 'tcp';
|
||||
|
||||
return array_map(static function (string $elem) {
|
||||
return 'tcp://' . $elem;
|
||||
return array_map(static function (string $elem) use ($scheme) {
|
||||
return "{$scheme}://" . $elem;
|
||||
}, $endpoints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Connection\Resource\Exception\StreamInitException;
|
||||
use PredisTestCase;
|
||||
|
||||
class SSLTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testExecuteCommandOverSSLConnection()
|
||||
{
|
||||
$redis = $this->createClient();
|
||||
$this->assertEquals('PONG', $redis->ping());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testExecuteCommandOverSSLConnectionFailsOnIncorrectCertificate()
|
||||
{
|
||||
$redis = new Client($this->getDefaultParametersArray() + [
|
||||
'ssl' => ['cafile' => '/tmp/invalid.crt', 'verify_peer' => true, 'verify_peer_name' => false]]
|
||||
);
|
||||
|
||||
$this->expectException(StreamInitException::class);
|
||||
$this->expectExceptionMessage('Error while switching to encrypted communication');
|
||||
|
||||
$redis->ping();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testExecuteCommandOverSSLConnectionWithoutSSLConfig()
|
||||
{
|
||||
$redis = new Client($this->getDefaultParametersArray());
|
||||
|
||||
$this->expectException(StreamInitException::class);
|
||||
$this->expectExceptionMessage('Error while switching to encrypted communication');
|
||||
|
||||
$redis->ping();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group cluster
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testClusterExecuteCommandOverSSLConnection()
|
||||
{
|
||||
$redis = $this->createClient();
|
||||
$redis->set('foo', 'bar');
|
||||
$this->assertEquals('bar', $redis->get('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group cluster
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testClusterExecuteCommandOverSSLConnectionFailsOnIncorrectCertificate()
|
||||
{
|
||||
$redis = new Client($this->getDefaultParametersArray(), [
|
||||
'cluster' => 'redis',
|
||||
'parameters' => [
|
||||
'ssl' => ['cafile' => '/tmp/invalid.crt', 'verify_peer' => true, 'verify_peer_name' => false],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->expectException(StreamInitException::class);
|
||||
$this->expectExceptionMessage('Error while switching to encrypted communication');
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group ssl
|
||||
* @group cluster
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.2.0
|
||||
* @return void
|
||||
*/
|
||||
public function testClusterExecuteCommandOverSSLConnectionWithoutSSLConfig()
|
||||
{
|
||||
$redis = new Client($this->getDefaultParametersArray(), [
|
||||
'cluster' => 'redis',
|
||||
]);
|
||||
|
||||
$this->expectException(StreamInitException::class);
|
||||
$this->expectExceptionMessage('Error while switching to encrypted communication');
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user