File: /var/www/payments-gateway/vendor/symfony/maker-bundle/src/Security/SecurityConfigUpdater.php
<?php
/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\MakerBundle\Security;
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
use Symfony\Component\HttpKernel\Log\Logger;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
/**
* @author Ryan Weaver <ryan@symfonycasts.com>
* @author Jesse Rushlow <jr@rushlow.dev>
*
* @internal
*/
final class SecurityConfigUpdater
{
private ?YamlSourceManipulator $manipulator = null;
public function __construct(
private ?Logger $ysmLogger = null,
) {
}
public function updateForFormLogin(string $yamlSource, string $firewallToUpdate, string $loginPath, string $checkPath): string
{
$newData = $this->createYamlSourceManipulator($yamlSource);
$newData['security']['firewalls'][$firewallToUpdate]['form_login']['login_path'] = $loginPath;
$newData['security']['firewalls'][$firewallToUpdate]['form_login']['check_path'] = $checkPath;
$newData['security']['firewalls'][$firewallToUpdate]['form_login']['enable_csrf'] = true;
return $this->getYamlContentsFromData($newData);
}
public function updateForJsonLogin(string $yamlSource, string $firewallToUpdate, string $checkPath): string
{
$data = $this->createYamlSourceManipulator($yamlSource);
$data['security']['firewalls'][$firewallToUpdate]['json_login']['check_path'] = $checkPath;
return $this->getYamlContentsFromData($data);
}
/**
* Updates security.yaml contents based on a new User class.
*/
public function updateForUserClass(string $yamlSource, UserClassConfiguration $userConfig, string $userClass): string
{
$this->createYamlSourceManipulator($yamlSource);
$this->updateProviders($userConfig, $userClass);
if ($userConfig->hasPassword()) {
$this->updatePasswordHashers($userClass);
}
$contents = $this->manipulator->getContents();
$this->manipulator = null;
return $contents;
}
public function updateForAuthenticator(string $yamlSource, string $firewallName, $chosenEntryPoint, string $authenticatorClass, bool $logoutSetup, bool $supportRememberMe, bool $alwaysRememberMe): string
{
$this->createYamlSourceManipulator($yamlSource);
$newData = $this->manipulator->getData();
if (!isset($newData['security']['firewalls'])) {
if ($newData['security']) {
$newData['security']['_firewalls'] = $this->manipulator->createEmptyLine();
}
$newData['security']['firewalls'] = [];
}
if (!isset($newData['security']['firewalls'][$firewallName])) {
$newData['security']['firewalls'][$firewallName] = ['lazy' => true];
}
$firewall = $newData['security']['firewalls'][$firewallName];
if (isset($firewall['custom_authenticator'])) {
if (\is_array($firewall['custom_authenticator'])) {
$firewall['custom_authenticator'][] = $authenticatorClass;
} else {
$stringValue = $firewall['custom_authenticator'];
$firewall['custom_authenticator'] = [];
$firewall['custom_authenticator'][] = $stringValue;
$firewall['custom_authenticator'][] = $authenticatorClass;
}
} else {
$firewall['custom_authenticator'] = $authenticatorClass;
}
if (!isset($firewall['entry_point']) && $chosenEntryPoint) {
$firewall['entry_point_empty_line'] = $this->manipulator->createEmptyLine();
$firewall['entry_point_comment'] = $this->manipulator->createCommentLine(
' the entry_point start() method determines what happens when an anonymous user accesses a protected page'
);
$firewall['entry_point'] = $authenticatorClass;
}
if (!isset($firewall['logout']) && $logoutSetup) {
$firewall['logout'] = ['path' => 'app_logout'];
$firewall['logout'][] = $this->manipulator->createCommentLine(
' where to redirect after logout'
);
$firewall['logout'][] = $this->manipulator->createCommentLine(
' target: app_any_route'
);
}
if ($supportRememberMe) {
if (!isset($firewall['remember_me'])) {
$firewall['remember_me_empty_line'] = $this->manipulator->createEmptyLine();
$firewall['remember_me'] = [
'secret' => '%kernel.secret%',
'lifetime' => 604800,
'path' => '/',
];
if (!$alwaysRememberMe) {
$firewall['remember_me'][] = $this->manipulator->createCommentLine(' by default, the feature is enabled by checking a checkbox in the');
$firewall['remember_me'][] = $this->manipulator->createCommentLine(' login form, uncomment the following line to always enable it.');
}
} else {
$firewall['remember_me']['secret'] ??= '%kernel.secret%';
$firewall['remember_me']['lifetime'] ??= 604800;
$firewall['remember_me']['path'] ??= '/';
}
if ($alwaysRememberMe) {
$firewall['remember_me']['always_remember_me'] = true;
} else {
$firewall['remember_me'][] = $this->manipulator->createCommentLine('always_remember_me: true');
}
}
$newData['security']['firewalls'][$firewallName] = $firewall;
if (!isset($firewall['logout']) && $logoutSetup) {
$this->configureLogout($newData, $firewallName);
return $this->manipulator->getContents();
}
$this->manipulator->setData($newData);
return $this->manipulator->getContents();
}
public function updateForLogout(string $yamlSource, string $firewallName): string
{
$this->createYamlSourceManipulator($yamlSource);
$this->configureLogout($this->manipulator->getData(), $firewallName);
return $this->manipulator->getContents();
}
/**
* @legacy This can be removed once we deprecate/remove `make:auth`
*/
private function configureLogout(array $securityData, string $firewallName): void
{
$securityData['security']['firewalls'][$firewallName]['logout'] = ['path' => 'app_logout'];
$securityData['security']['firewalls'][$firewallName]['logout'][] = $this->manipulator->createCommentLine(
' where to redirect after logout'
);
$securityData['security']['firewalls'][$firewallName]['logout'][] = $this->manipulator->createCommentLine(
' target: app_any_route'
);
$this->manipulator->setData($securityData);
}
private function createYamlSourceManipulator(string $yamlSource): array
{
$this->manipulator = new YamlSourceManipulator($yamlSource);
if (null !== $this->ysmLogger) {
$this->manipulator->setLogger($this->ysmLogger);
}
$this->normalizeSecurityYamlFile();
return $this->manipulator->getData();
}
private function getYamlContentsFromData(array $yamlData): string
{
$this->manipulator->setData($yamlData);
return $this->manipulator->getContents();
}
private function normalizeSecurityYamlFile(): void
{
if (!isset($this->manipulator->getData()['security'])) {
$newData = $this->manipulator->getData();
$newData['security'] = [];
$this->manipulator->setData($newData);
}
}
private function updateProviders(UserClassConfiguration $userConfig, string $userClass): void
{
$this->removeMemoryProviderIfIsSingleConfigured();
$newData = $this->manipulator->getData();
if ($newData['security'] && !\array_key_exists('providers', $newData['security'])) {
$newData['security']['_providers'] = $this->manipulator->createEmptyLine();
}
$newData['security']['providers']['__'] = $this->manipulator->createCommentLine(
' used to reload user from session & other features (e.g. switch_user)'
);
if ($userConfig->isEntity()) {
$newData['security']['providers']['app_user_provider'] = [
'entity' => [
'class' => $userClass,
'property' => $userConfig->getIdentityPropertyName(),
],
];
} else {
if (!$userConfig->getUserProviderClass()) {
throw new \LogicException('User provider class must be set for non-entity user.');
}
$newData['security']['providers']['app_user_provider'] = [
'id' => $userConfig->getUserProviderClass(),
];
}
$this->manipulator->setData($newData);
}
private function updatePasswordHashers(string $userClass): void
{
$newData = $this->manipulator->getData();
if (isset($newData['security']['encoders'])) {
throw new \RuntimeException('Password Encoders are no longer supported by MakerBundle. Please update your "config/packages/security.yaml" file to use Password Hashers instead.');
}
// The security-bundle recipe sets the password hasher via Flex. If it exists, move on...
if (isset($newData['security']['password_hashers'][PasswordAuthenticatedUserInterface::class])) {
return;
}
// by convention, password_hashers are put before the user provider option
$providersIndex = array_search('providers', array_keys($newData['security']));
if (false === $providersIndex) {
$newData['security'] = ['password_hashers' => []] + $newData['security'];
} else {
$newData['security'] = array_merge(
\array_slice($newData['security'], 0, $providersIndex),
['password_hashers' => []],
\array_slice($newData['security'], $providersIndex)
);
}
$newData['security']['password_hashers'][$userClass] = [
'algorithm' => 'auto',
];
$newData['security']['password_hashers']['_'] = $this->manipulator->createEmptyLine();
$this->manipulator->setData($newData);
}
private function removeMemoryProviderIfIsSingleConfigured(): void
{
if (!$this->isSingleInMemoryProviderConfigured()) {
return;
}
$newData = $this->manipulator->getData();
$memoryProviderName = array_keys($newData['security']['providers'])[0];
$newData['security']['providers'] = [];
foreach ($newData['security']['firewalls'] as &$firewall) {
if (($firewall['provider'] ?? null) === $memoryProviderName) {
$firewall['provider'] = 'app_user_provider';
}
}
$this->manipulator->setData($newData);
}
private function isSingleInMemoryProviderConfigured(): bool
{
if (!isset($this->manipulator->getData()['security']['providers'])) {
return false;
}
$providersConfig = $this->manipulator->getData()['security']['providers'];
if (1 !== \count($providersConfig)) {
return false;
}
$firstProviderConfig = array_values($providersConfig)[0];
return \array_key_exists('memory', $firstProviderConfig);
}
}