Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1,471 changes: 1 addition & 1,470 deletions phpstan-baseline.neon

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use function is_array;
use function is_iterable;
use function is_string;

final class AuthenticationCompilerPass implements CompilerPassInterface
{
Expand All @@ -39,24 +42,39 @@ private function registerLoginRouteLoader(ContainerBuilder $container): void
$routeLoader = $container->getDefinition(LoginPageRouteLoader::class);
$firewalls = $container->getParameter('security.firewalls');

if (! is_iterable($firewalls)) {
return;
}

$authenticators = [];

foreach ($firewalls as $firewall) {
if (! is_string($firewall)) {
continue;
}

if (! $container->hasDefinition('security.authenticator.form_login.' . $firewall)) {
continue;
}

$authenticator = $container->getDefinition('security.authenticator.form_login.' . $firewall);
$authenticators[$firewall] = $authenticator->getArgument(4) + [
$options = $authenticator->getArgument(4);
if (! is_array($options)) {
continue;
}

$authenticators[$firewall] = $options + [
'remember_me_parameter' => null,
'always_remember_me' => false,
];

if ($container->hasDefinition('security.authenticator.remember_me_handler.' . $firewall)) {
$rememberMeArguments = $container->getDefinition('security.authenticator.remember_me_handler.' . $firewall)->getArgument(3);

$authenticators[$firewall]['remember_me_parameter'] = $rememberMeArguments['remember_me_parameter'];
$authenticators[$firewall]['always_remember_me'] = $rememberMeArguments['always_remember_me'];
if (is_array($rememberMeArguments)) {
$authenticators[$firewall]['remember_me_parameter'] = $rememberMeArguments['remember_me_parameter'] ?? null;
$authenticators[$firewall]['always_remember_me'] = $rememberMeArguments['always_remember_me'] ?? false;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use function is_array;

final class MenuCompilerPass implements CompilerPassInterface
{
Expand All @@ -37,6 +38,9 @@ public function process(ContainerBuilder $container): void

foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
if (! is_array($attributes)) {
continue;
}

$wrapperDefinition = (new Definition(Closure::class))
->addArgument([new Reference($id), $attributes['method']])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ final class TwoFactorExtension
/**
* @param array{name: string, base_template: string} $config
*/
public static function enable(ContainerBuilder $container, array $config = []): void
public static function enable(ContainerBuilder $container, array $config): void
{
$container
->setDefinition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Knp\Menu\Provider\MenuProviderInterface;
use Override;
use ReflectionMethod;
use Reflector;
use SolidWorx\Platform\PlatformBundle\Attributes\Menu\MenuBuilder;
use SolidWorx\Platform\PlatformBundle\Config\PlatformConfiguration;
use SolidWorx\Platform\PlatformBundle\Controller\Security\ResendTwoFactorCode;
Expand Down Expand Up @@ -68,10 +69,14 @@ public function load(array $configs, ContainerBuilder $container): void
$container->registerForAutoconfiguration(MenuProviderInterface::class)
->addTag('knp_menu.provider');

$container->registerAttributeForAutoconfiguration(MenuBuilder::class, static function (ChildDefinition $definition, MenuBuilder $attribute, ReflectionMethod $reflectionMethod): void {
$container->registerAttributeForAutoconfiguration(MenuBuilder::class, static function (ChildDefinition $definition, MenuBuilder $attribute, Reflector $reflector): void {
if (! $reflector instanceof ReflectionMethod) {
return;
}

$definition->addTag(Util::tag('menu.builder'), [
'alias' => $attribute->name,
'method' => $reflectionMethod->getName(),
'method' => $reflector->getName(),
'priority' => $attribute->priority,
'role' => $attribute->role,
]);
Expand Down
13 changes: 12 additions & 1 deletion src/Bundle/Platform/Feature/FeatureValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

namespace SolidWorx\Platform\PlatformBundle\Feature;

use Stringable;
use function is_array;
use function is_bool;
use function is_int;
use function is_scalar;

final readonly class FeatureValue
{
Expand Down Expand Up @@ -75,7 +77,16 @@ public function asBool(): bool
public function asString(): string
{
if (is_array($this->value)) {
return implode(',', $this->value);
$parts = [];
foreach ($this->value as $item) {
if (is_scalar($item)) {
$parts[] = (string) $item;
} elseif ($item instanceof Stringable) {
$parts[] = (string) $item;
}
}

return implode(',', $parts);
}

if (is_bool($this->value)) {
Expand Down
6 changes: 3 additions & 3 deletions src/Bundle/Platform/Form/Type/Security/LoginType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;

/**
* @extends AbstractType<array{code: string|null}>
*/
final class LoginType extends AbstractType

Check failure on line 25 in src/Bundle/Platform/Form/Type/Security/LoginType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Class SolidWorx\Platform\PlatformBundle\Form\Type\Security\LoginType extends generic class Symfony\Component\Form\AbstractType but does not specify its types: TData
{
/**
* @param array{username_parameter: string, password_parameter: string} $options
*/
#[Override]

Check failure on line 30 in src/Bundle/Platform/Form/Type/Security/LoginType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #2 $options (array{username_parameter: string, password_parameter: string}) of method SolidWorx\Platform\PlatformBundle\Form\Type\Security\LoginType::buildForm() should be contravariant with parameter $options (array<string, mixed>) of method Symfony\Component\Form\FormTypeInterface<mixed>::buildForm()

Check failure on line 30 in src/Bundle/Platform/Form/Type/Security/LoginType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #2 $options (array{username_parameter: string, password_parameter: string}) of method SolidWorx\Platform\PlatformBundle\Form\Type\Security\LoginType::buildForm() should be contravariant with parameter $options (array<string, mixed>) of method Symfony\Component\Form\AbstractType<mixed>::buildForm()
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;

/**
* @extends AbstractType<array{code: string|null}>
*/
final class TwoFactorVerifyType extends AbstractType

Check failure on line 25 in src/Bundle/Platform/Form/Type/Security/TwoFactorVerifyType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Class SolidWorx\Platform\PlatformBundle\Form\Type\Security\TwoFactorVerifyType extends generic class Symfony\Component\Form\AbstractType but does not specify its types: TData
{
/**
* @param array{secret: string} $options
*/
#[Override]

Check failure on line 30 in src/Bundle/Platform/Form/Type/Security/TwoFactorVerifyType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #2 $options (array{secret: string}) of method SolidWorx\Platform\PlatformBundle\Form\Type\Security\TwoFactorVerifyType::buildForm() should be contravariant with parameter $options (array<string, mixed>) of method Symfony\Component\Form\FormTypeInterface<mixed>::buildForm()

Check failure on line 30 in src/Bundle/Platform/Form/Type/Security/TwoFactorVerifyType.php

View workflow job for this annotation

GitHub Actions / PHPStan

Parameter #2 $options (array{secret: string}) of method SolidWorx\Platform\PlatformBundle\Form\Type\Security\TwoFactorVerifyType::buildForm() should be contravariant with parameter $options (array<string, mixed>) of method Symfony\Component\Form\AbstractType<mixed>::buildForm()
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
Expand All @@ -44,7 +44,7 @@
],
'constraints' => [
new NotBlank(),
new TwoFactorCode(secret: (string) $options['secret']),
new TwoFactorCode(secret: $options['secret']),
],
])
->add('secret', HiddenType::class, [
Expand Down
74 changes: 58 additions & 16 deletions src/Bundle/Platform/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
Expand All @@ -34,7 +35,9 @@
use function file_exists;
use function glob;
use function implode;
use function is_array;
use function is_file;
use function is_string;
use function pathinfo;
use function sprintf;

Expand Down Expand Up @@ -71,13 +74,15 @@
}

#[Override]
public function registerBundles(): iterable

Check failure on line 77 in src/Bundle/Platform/Kernel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Return type of method SolidWorx\Platform\PlatformBundle\Kernel::registerBundles() has typehint with deprecated interface Symfony\Component\HttpKernel\Bundle\BundleInterface: since Symfony 8.1, use Symfony\Component\DependencyInjection\Kernel\BundleInterface instead
{
yield from $this->registerBundlesTrait();

$platformConfig = $this->rawConfig['platform'] ?? [];
foreach ($this->registerBundlesTrait() as $bundle) {
if ($bundle instanceof BundleInterface) {

Check failure on line 80 in src/Bundle/Platform/Kernel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Instanceof references deprecated interface Symfony\Component\HttpKernel\Bundle\BundleInterface: since Symfony 8.1, use Symfony\Component\DependencyInjection\Kernel\BundleInterface instead
yield $bundle;
}
}

if (($platformConfig['security']['two_factor']['enabled'] ?? false) === true) {
if ($this->isTwoFactorEnabled()) {
yield new SchebTwoFactorBundle();
}

Expand Down Expand Up @@ -106,7 +111,7 @@
$section = $key !== ''
? ($this->rawConfig[$key] ?? [])
: ($this->rawConfig['platform'] ?? []);
$bundle->setPlatformRawConfig($section);
$bundle->setPlatformRawConfig(self::toConfigArray($section));
}

$this->bundles[$name] = $bundle;
Expand All @@ -120,6 +125,26 @@
$routes->import('.', '_solidworx_platform_auth_routes');
}

private function isTwoFactorEnabled(): bool
{
$platformConfig = $this->rawConfig['platform'] ?? [];
if (! is_array($platformConfig)) {
return false;
}

$security = $platformConfig['security'] ?? [];
if (! is_array($security)) {
return false;
}

$twoFactor = $security['two_factor'] ?? [];
if (! is_array($twoFactor)) {
return false;
}

return ($twoFactor['enabled'] ?? false) === true;
}

private function processPlatformConfig(): void
{
if ($this->rawConfig !== null) {
Expand All @@ -139,21 +164,38 @@
$ext = pathinfo($configFile, PATHINFO_EXTENSION);

$this->rawConfig = match ($ext) {
'yaml', 'yml' => Yaml::parseFile($configFile) ?? [],
'json' => (static function (string $path): array {
$decoded = json_decode((string) file_get_contents($path), true, 512, JSON_THROW_ON_ERROR);
return is_array($decoded) ? $decoded : [];
})($configFile),
'php' => (static function (string $path): array {
$result = require $path;
return is_array($result) ? $result : [];
})($configFile),
'yaml', 'yml' => self::toConfigArray(Yaml::parseFile($configFile)),
'json' => self::toConfigArray(json_decode((string) file_get_contents($configFile), true, 512, JSON_THROW_ON_ERROR)),
'php' => self::toConfigArray(require $configFile),
default => throw new RuntimeException(sprintf('Unsupported platform configuration file format: .%s', $ext)),
};

$this->publishPlatformConfigState();
}

/**
* Normalises a decoded configuration value into a string-keyed map.
*
* The root of a configuration file is always an associative map, so any non-array
* value is treated as empty and the keys are normalised to strings.
*
* @return array<string, mixed>
*/
private static function toConfigArray(mixed $value): array
{
if (! is_array($value)) {
return [];
}

$config = [];

foreach ($value as $key => $item) {
$config[(string) $key] = $item;
}

return $config;
}

/**
* Publishes the parsed `platform:` section so compile-time helpers (e.g. the security
* config helpers) can read it while the container is being built.
Expand All @@ -169,8 +211,8 @@
{
// Allow an explicit path override via environment variable
$envOverride = $_ENV['PLATFORM_CONFIG_FILE'] ?? $_SERVER['PLATFORM_CONFIG_FILE'] ?? null;
if ($envOverride !== null && is_file((string) $envOverride)) {
return (string) $envOverride;
if (is_string($envOverride) && is_file($envOverride)) {
return $envOverride;
}

$glob = $this->getProjectDir() . '/' . self::DEFAULT_CONFIG_GLOB;
Expand All @@ -178,7 +220,7 @@
$configFiles = [];

if (defined('GLOB_BRACE')) {
$configFiles = glob($glob, GLOB_BRACE) ?: [];

Check failure on line 223 in src/Bundle/Platform/Kernel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Short ternary operator is not allowed. Use null coalesce operator if applicable or consider using long ternary.
} else {
foreach (['yml', 'yaml', 'json', 'php'] as $ext) {
$candidate = $this->getProjectDir() . '/platform.' . $ext;
Expand Down
4 changes: 3 additions & 1 deletion src/Bundle/Platform/Menu/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Options
* route?: string,
* routeParameters?: array<string, scalar>,
* routeAbsolute?: bool,
* extras?: array{role?: string, icon?: string},
* }
*/
private array $options = [];
Expand All @@ -33,7 +34,7 @@ public static function create(): self
return new self();
}

/***
/**
* @param array<string, scalar> $parameters
*/
public function route(string $route, array $parameters = [], bool $absolute = false): self
Expand Down Expand Up @@ -63,6 +64,7 @@ public function icon(string $icon): self
* route?: string,
* routeParameters?: array<string, scalar>,
* routeAbsolute?: bool,
* extras?: array{role?: string, icon?: string},
* }
*/
public function build(): array
Expand Down
2 changes: 1 addition & 1 deletion src/Bundle/Platform/Menu/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
final class Provider implements MenuProviderInterface
{
/**
* @var array<string, SplPriorityQueue<int, callable>>
* @var array<string, SplPriorityQueue<array{int, int}, callable>>
*/
private array $list = [];

Expand Down
5 changes: 5 additions & 0 deletions src/Bundle/Platform/Model/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use EmailChecker\Constraints as EmailCheckerAssert;
use LogicException;
use Override;
use SolidWorx\Platform\PlatformBundle\Contracts\Security\TwoFactor\UserTwoFactorInterface;
use SolidWorx\Platform\PlatformBundle\Security\TwoFactor\Traits\UserTwoFactor;
Expand Down Expand Up @@ -135,6 +136,10 @@ public function getId(): ?Ulid
#[Override]
public function getUserIdentifier(): string
{
if ($this->email === null || $this->email === '') {
throw new LogicException('Cannot resolve the user identifier because the email is not set.');
}

return $this->email;
}

Expand Down
Loading
Loading