Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ To use the Global Site Connector you need to add some config parameters to the c
Config.php parameters to operate the server in master mode:

````
// can be chosen freely, you just have to make sure the master and
// all slaves have the same key. Also make sure to choose a strong shared secret.
'gss.jwt.key' => 'random-key',
// Shared secret used to sign JWT tokens between master and slave nodes.
// IMPORTANT: Must be at least 32 characters long (required by HS256 per RFC 7518).
// Must be identical on master and all slave nodes.
// Example: use `openssl rand -base64 32` to generate a strong key.
'gss.jwt.key' => 'random-key-at-least-32-characters',

// operation mode
'gss.mode' => 'master',
Expand All @@ -50,9 +52,10 @@ Config.php parameters to operate the server in master mode:
Config parameters to operate the server in slave mode:

````
// can be chosen freely, you just have to make sure the master and
// all slaves have the same key. Also make sure to choose a strong shared secret.
'gss.jwt.key' => 'random-key',
// Shared secret used to sign JWT tokens between master and slave nodes.
// IMPORTANT: Must be at least 32 characters long (required by HS256 per RFC 7518).
// Must be identical on master and all slave nodes.
'gss.jwt.key' => 'random-key-at-least-32-characters',

// operation mode
'gss.mode' => 'slave',
Expand Down
22 changes: 13 additions & 9 deletions lib/GlobalSiteSelector.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ class GlobalSiteSelector {
public const MASTER = 'master';
public const SLAVE = 'slave';

/** @var IConfig */
private $config;
public const MIN_JWT_KEY_LENGTH = 32;

/**
* GlobalSiteSelector constructor.
*
* @param IConfig $config
*/
public function __construct(IConfig $config) {
public function __construct(
private IConfig $config,
) {
$this->config = $config;
}

Expand Down Expand Up @@ -64,10 +60,18 @@ public function isSlave(): bool {
* @return string
*/
public function getJwtKey(): string {
// TODO: returns exception if non-existant
return $this->config->getSystemValueString('gss.jwt.key', '');
}

/**
* Validate that the JWT key meets minimum length requirements.
* HS256 requires a key of at least 256 bits (32 bytes) per RFC 7518 §3.2.
*/
public function isJwtKeyValid(): bool {
$key = $this->getJwtKey();
return $key !== '' && strlen($key) >= self::MIN_JWT_KEY_LENGTH;
}

/**
* get the URL of the global site selector master
*
Expand Down
10 changes: 10 additions & 0 deletions lib/Master.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ protected function redirectUser($uid, $password, $location, array $options = [])
* @return string
*/
protected function createJwt($uid, $password, $options) {
if (!$this->gss->isJwtKeyValid()) {
$this->logger->error(
'gss.jwt.key is too short: HS256 requires at least '
. GlobalSiteSelector::MIN_JWT_KEY_LENGTH . ' characters (per RFC 7518). '
. 'Current key length: ' . strlen($this->gss->getJwtKey()) . '. '
. 'Please update gss.jwt.key in config.php on all nodes.',
['app' => Application::APP_ID]
);
}

$token = [
'uid' => $uid,
'password' => $this->crypto->encrypt($password, $this->gss->getJwtKey()),
Expand Down
10 changes: 10 additions & 0 deletions lib/Slave.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ protected function checkConfiguration(): bool {
return false;
}

if (!$this->gss->isJwtKeyValid()) {
$this->logger->error(
'gss.jwt.key is too short: HS256 requires at least '
. GlobalSiteSelector::MIN_JWT_KEY_LENGTH . ' characters (per RFC 7518). '
. 'Current key length: ' . strlen($this->authKey) . '. '
. 'Please update gss.jwt.key in config.php on all nodes.',
['app' => Application::APP_ID]
);
}

return true;
}

Expand Down
21 changes: 21 additions & 0 deletions tests/unit/lib/GlobalSiteSelectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,25 @@ public function testGetLookupServerUrl() {

$this->assertSame('result', $result);
}

public function testIsJwtKeyValidWithShortKey() {
$this->config->method('getSystemValueString')
->with('gss.jwt.key', '')->willReturn('short-key');

$this->assertFalse($this->gss->isJwtKeyValid());
}

public function testIsJwtKeyValidWithEmptyKey() {
$this->config->method('getSystemValueString')
->with('gss.jwt.key', '')->willReturn('');

$this->assertFalse($this->gss->isJwtKeyValid());
}

public function testIsJwtKeyValidWithValidKey() {
$this->config->method('getSystemValueString')
->with('gss.jwt.key', '')->willReturn('this-key-is-at-least-32-characters-long!');

$this->assertTrue($this->gss->isJwtKeyValid());
}
}
Loading