Skip to content
Open
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
138 changes: 128 additions & 10 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,93 @@ public function getConfigTreeBuilder(): TreeBuilder
->useAttributeAsKey('name')
->normalizeKeys(false)
->arrayPrototype()
->beforeNormalization()
->always(static function ($v) {
if (!\is_array($v)) {
return $v;
}

// Reject mixing legacy and new config
$hasLegacy = isset($v['jwt']) || isset($v['jwt_provider']);
$hasNew = isset($v['publisher']) || isset($v['subscriber']);
if ($hasLegacy && $hasNew) {
throw new \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException('"jwt"/"jwt_provider" and "publisher"/"subscriber" cannot be used together. Migrate to "publisher"/"subscriber".');
}

// Normalize legacy jwt_provider → publisher.provider
if (isset($v['jwt_provider'])) {
$v['publisher']['provider'] = $v['jwt_provider'];
unset($v['jwt_provider']);
}

// Normalize legacy jwt → publisher + subscriber
if (isset($v['jwt'])) {
$jwt = $v['jwt'];

// jwt as string shorthand
if (\is_string($jwt)) {
$jwt = ['value' => $jwt];
}

if (!isset($v['publisher'])) {
$publisher = [];

if (isset($jwt['value'])) {
$publisher['value'] = $jwt['value'];
}
if (isset($jwt['provider'])) {
$publisher['provider'] = $jwt['provider'];
}
if (isset($jwt['factory'])) {
$publisher['factory'] = $jwt['factory'];
}
if (isset($jwt['secret'])) {
$publisher['secret'] = $jwt['secret'];
}
if (isset($jwt['algorithm'])) {
$publisher['algorithm'] = $jwt['algorithm'];
}
if (isset($jwt['passphrase'])) {
$publisher['passphrase'] = $jwt['passphrase'];
}
if (isset($jwt['publish'])) {
$publisher['topics'] = $jwt['publish'];
}

$v['publisher'] = $publisher;
}

if (!isset($v['subscriber'])) {
$subscriber = [];

if (isset($jwt['factory'])) {
$subscriber['factory'] = $jwt['factory'];
}
if (isset($jwt['secret'])) {
$subscriber['secret'] = $jwt['secret'];
}
if (isset($jwt['algorithm'])) {
$subscriber['algorithm'] = $jwt['algorithm'];
}
if (isset($jwt['passphrase'])) {
$subscriber['passphrase'] = $jwt['passphrase'];
}
if (isset($jwt['subscribe'])) {
$subscriber['topics'] = $jwt['subscribe'];
}

// Only set subscriber if there's something to subscribe with
if (!empty($subscriber)) {
$v['subscriber'] = $subscriber;
}
}

unset($v['jwt']);
}

return $v;
})
->end()
->children()
->scalarNode('url')->info('URL of the hub\'s publish endpoint')->example('https://demo.mercure.rocks/.well-known/mercure');

Expand All @@ -53,6 +140,36 @@ public function getConfigTreeBuilder(): TreeBuilder
}

$publicUrlNode->end()
->arrayNode('publisher')
->info('Publisher JWT configuration (TokenProviderInterface).')
->children()
->scalarNode('value')->info('Static JSON Web Token to use to publish to this hub.')->end()
->scalarNode('provider')->info('The ID of a service implementing TokenProviderInterface.')->end()
->scalarNode('factory')->info('The ID of a service implementing TokenFactoryInterface, wrapped as FactoryTokenProvider.')->end()
->arrayNode('topics')
->beforeNormalization()->castToArray()->end()
->scalarPrototype()->end()
->info('A list of topics for the mercure.publish claim in the publisher JWT.')
->end()
->scalarNode('secret')->info('The JWT secret to use.')->example('!ChangeMe!')->end()
->scalarNode('passphrase')->info('The JWT secret passphrase.')->defaultValue('')->end()
->scalarNode('algorithm')->info('The algorithm to use to sign the JWT.')->defaultValue('hmac.sha256')->end()
->end()
->end()
->arrayNode('subscriber')
->info('Subscriber JWT configuration (TokenFactoryInterface).')
->children()
->scalarNode('factory')->info('The ID of a service implementing TokenFactoryInterface.')->end()
->arrayNode('topics')
->beforeNormalization()->castToArray()->end()
->scalarPrototype()->end()
->info('A list of topics for the mercure.subscribe claim in the publisher JWT.')
->end()
->scalarNode('secret')->info('The JWT secret to use.')->example('!ChangeMe!')->end()
->scalarNode('passphrase')->info('The JWT secret passphrase.')->defaultValue('')->end()
->scalarNode('algorithm')->info('The algorithm to use to sign the JWT.')->defaultValue('hmac.sha256')->end()
->end()
->end()
->arrayNode('jwt')
->beforeNormalization()
->ifString()
Expand All @@ -62,7 +179,8 @@ public function getConfigTreeBuilder(): TreeBuilder
];
})
->end()
->info('JSON Web Token configuration.')
->setDeprecated('symfony/mercure-bundle', '0.5', 'The child node "%node%" at path "%path%" is deprecated, use "publisher" and "subscriber" instead.')
->info('JSON Web Token configuration (deprecated, use "publisher" and "subscriber" instead).')
->children()
->scalarNode('value')->info('JSON Web Token to use to publish to this hub.')->end()
->scalarNode('provider')->info('The ID of a service to call to provide the JSON Web Token.')->end()
Expand All @@ -84,25 +202,25 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->scalarNode('jwt_provider')
->info('The ID of a service to call to generate the JSON Web Token.')
->setDeprecated('symfony/mercure-bundle', '0.3', 'The child node "%node%" at path "%path%" is deprecated, use "jwt.provider" instead.')
->setDeprecated('symfony/mercure-bundle', '0.3', 'The child node "%node%" at path "%path%" is deprecated, use "publisher.provider" instead.')
->end()
->scalarNode('bus')->info('Name of the Messenger bus where the handler for this hub must be registered. Default to the default bus if Messenger is enabled.')->end()
->end()
->validate()
->ifTrue(function ($v) { return isset($v['jwt'], $v['jwt_provider']); })
->thenInvalid('"jwt" and "jwt_provider" cannot be used together.')
->ifTrue(static function ($v) { return isset($v['url']) && !isset($v['publisher']); })
->thenInvalid('You must specify a "publisher" configuration.')
->end()
->validate()
->ifTrue(function ($v) { return isset($v['url']) && !isset($v['jwt']) && !isset($v['jwt_provider']); })
->thenInvalid('You must specify at least one of "jwt", and "jwt_provider".')
->ifTrue(static function ($v) { return isset($v['publisher']) && !isset($v['publisher']['value']) && !isset($v['publisher']['provider']) && !isset($v['publisher']['factory']) && !isset($v['publisher']['secret']); })
->thenInvalid('You must specify at least one of "publisher.value", "publisher.provider", "publisher.factory", and "publisher.secret".')
->end()
->validate()
->ifTrue(function ($v) { return isset($v['jwt']['value'], $v['jwt']['provider']); })
->thenInvalid('"jwt.value" and "jwt.provider" cannot be used together.')
->ifTrue(static function ($v) { return isset($v['publisher']['value'], $v['publisher']['provider']); })
->thenInvalid('"publisher.value" and "publisher.provider" cannot be used together.')
->end()
->validate()
->ifTrue(function ($v) { return isset($v['jwt']) && !isset($v['jwt']['value']) && !isset($v['jwt']['provider']) && !isset($v['jwt']['factory']) && !isset($v['jwt']['secret']); })
->thenInvalid('You must specify at least one of "jwt.value", "jwt.provider", "jwt.factory", and "jwt.secret".')
->ifTrue(static function ($v) { return isset($v['subscriber']) && !isset($v['subscriber']['factory']) && !isset($v['subscriber']['secret']); })
->thenInvalid('You must specify at least one of "subscriber.factory" and "subscriber.secret".')
->end()
->end()
->end()
Expand Down
Loading