Skip to content

Commit 6da74d0

Browse files
fengtantvdijen
andauthored
Add support for Redis-Sentinel (simplesamlphp#1699)
* Add support for Redis-Sentinel. * Add tests for redis sentinel store. * Documentation about Redis-Sentinel and password protection. * Fix markdown syntax brought up by github actions. * Add missing config parameter to unit tests. * Add missing return-type * Fix indentation * Fix incorrect configuration-methods Co-authored-by: Tim van Dijen <tvdijen@gmail.com>
1 parent 7e82542 commit 6da74d0

5 files changed

Lines changed: 89 additions & 13 deletions

File tree

config-templates/config.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,4 +1221,21 @@
12211221
* The prefix we should use on our Redis datastore.
12221222
*/
12231223
'store.redis.prefix' => 'SimpleSAMLphp',
1224+
1225+
/*
1226+
* The master group to use for Redis Sentinel.
1227+
*/
1228+
'store.redis.mastergroup' => 'mymaster',
1229+
1230+
/*
1231+
* The Redis Sentinel hosts.
1232+
* Example:
1233+
* array(
1234+
* 'tcp://[yoursentinel1]:[port]'
1235+
* 'tcp://[yoursentinel2]:[port]',
1236+
* 'tcp://[yoursentinel3]:[port]
1237+
* )
1238+
*/
1239+
'store.redis.sentinels' => [],
1240+
12241241
];

docs/simplesamlphp-maintenance.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,30 @@ For Redis instances that [require authentication](https://redis.io/commands/auth
179179
* If authentication is managed with the `requirepass` directive (legacy password protection): use the `store.redis.password` option
180180
* If authentication is managed with [ACL's](https://redis.io/docs/manual/security/acl/) (which are recommended as of Redis 6): use the `store.redis.password` and `store.redis.username` options
181181

182+
#### Redis-Sentinel
183+
184+
If your Redis servers are controlled by [Redis-Sentinel](https://redis.io/docs/manual/sentinel/), then configure your sentinels by setting `store.redis.sentinels` to
185+
186+
```php
187+
[
188+
'tcp://[yoursentinel1]:[port]',
189+
'tcp://[yoursentinel2]:[port]',
190+
'tcp://[yoursentinel3]:[port]',
191+
]
192+
```
193+
194+
If your sentinels are password-protected and use the same password as your Redis servers, then setting `store.redis.password` is enough. However if your sentinels use a different password than that of your Redis servers, then set the password of each sentinel:
195+
196+
```php
197+
[
198+
'tcp://[yoursentinel1]:[port]?password=[password1]',
199+
'tcp://[yoursentinel2]:[port]?password=[password2]',
200+
'tcp://[yoursentinel3]:[port]?password=[password3]',
201+
]
202+
```
203+
204+
Configure your master group by setting `store.redis.mastergroup` (`mymaster` by default).
205+
182206
## Metadata storage
183207

184208
Several metadata storage backends are available by default, including `flatfile`, `serialize`, `mdq` and

src/SimpleSAML/Store/RedisStore.php

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,38 @@ public function __construct(Client $redis = null)
4242
$username = $config->getOptionalString('store.redis.username', null);
4343
$database = $config->getOptionalInteger('store.redis.database', 0);
4444

45-
$redis = new Client(
46-
[
47-
'scheme' => 'tcp',
48-
'host' => $host,
49-
'port' => $port,
50-
'database' => $database,
51-
]
52-
+ (!empty($password) ? ['password' => $password] : [])
53-
+ (!empty($username) ? ['username' => $username] : []),
54-
[
55-
'prefix' => $prefix,
56-
]
57-
);
45+
$sentinels = $config->getOptionalArray('store.redis.sentinels', []);
46+
47+
if (empty($sentinels)) {
48+
$redis = new Client(
49+
[
50+
'scheme' => 'tcp',
51+
'host' => $host,
52+
'port' => $port,
53+
'database' => $database,
54+
]
55+
+ (!empty($username) ? ['username' => $username] : [])
56+
+ (!empty($password) ? ['password' => $password] : []),
57+
[
58+
'prefix' => $prefix,
59+
]
60+
);
61+
} else {
62+
$mastergroup = $config->getOptionalString('store.redis.mastergroup', 'mymaster');
63+
$redis = new Client(
64+
$sentinels,
65+
[
66+
'replication' => 'sentinel',
67+
'service' => $mastergroup,
68+
'prefix' => $prefix,
69+
'parameters' => [
70+
'database' => $database,
71+
]
72+
+ (!empty($username) ? ['username' => $username] : [])
73+
+ (!empty($password) ? ['password' => $password] : []),
74+
]
75+
);
76+
}
5877
}
5978

6079
$this->redis = $redis;

tests/src/SimpleSAML/Store/RedisStoreTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,21 @@ public function testRedisInstanceWithPasswordAndUsername(): void
143143
$this->assertInstanceOf(Store\RedisStore::class, $this->store);
144144
}
145145

146+
/**
147+
* @covers \SimpleSAML\Store::getInstance
148+
* @covers \SimpleSAML\Store\Redis::__construct
149+
* @test
150+
*/
151+
public function testRedisSentinelInstance(): void
152+
{
153+
$config = Configuration::loadFromArray(array(
154+
'store.type' => 'redis',
155+
'store.redis.prefix' => 'phpunit_',
156+
'store.redis.mastergroup' => 'phpunit_mastergroup',
157+
'store.redis.sentinels' => array('tcp://sentinel1', 'tcp://sentinel2', 'tcp://sentinel3'),
158+
), '[ARRAY]', 'simplesaml');
159+
$this->assertInstanceOf(Store\RedisStore::class, $this->store);
160+
}
146161

147162
/**
148163
* @test

tests/src/SimpleSAML/Store/StoreFactoryTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public function redisStore(): void
8686
Configuration::loadFromArray([
8787
'store.type' => 'redis',
8888
'store.redis.prefix' => 'phpunit_',
89+
'store.redis.sentinels' => [],
8990
], '[ARRAY]', 'simplesaml');
9091

9192
$config = Configuration::getInstance();

0 commit comments

Comments
 (0)