Skip to content

Commit

Permalink
Merge 4.x into 5.x
Browse files Browse the repository at this point in the history
  • Loading branch information
SonataCI authored Jul 16, 2024
2 parents b61e289 + 73492af commit 948053c
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 15 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [4.31.0](https://github.com/sonata-project/SonataAdminBundle/compare/4.30.2...4.31.0) - 2024-07-15
### Added
- [[#8192](https://github.com/sonata-project/SonataAdminBundle/pull/8192)] Enable security information mapping for `RoleSecurityHandler` ([@core23](https://github.com/core23))

### Fixed
- [[#8187](https://github.com/sonata-project/SonataAdminBundle/pull/8187)] Symfony 7.1 deprecation about `Symfony\Component\HttpKernel\DependencyInjection\Extension` usage ([@AirBair](https://github.com/AirBair))

## [4.30.2](https://github.com/sonata-project/SonataAdminBundle/compare/4.30.1...4.30.2) - 2024-05-07
### Fixed
- [[#8177](https://github.com/sonata-project/SonataAdminBundle/pull/8177)] Batch consistency ([@pietaj](https://github.com/pietaj))
Expand Down
2 changes: 0 additions & 2 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

use Rector\Config\RectorConfig;
use Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\AddSeeTestAnnotationRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Set\ValueObject\LevelSetList;
Expand All @@ -40,7 +39,6 @@
$rectorConfig->importShortClasses(false);
$rectorConfig->skip([
ExceptionHandlerTypehintRector::class,
AddSeeTestAnnotationRector::class,
PreferPHPUnitThisCallRector::class,
]);
};
4 changes: 2 additions & 2 deletions src/Admin/AbstractAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -1840,7 +1840,7 @@ protected function generateBaseRoutePattern(bool $isChildAdmin = false): string

preg_match(self::CLASS_REGEX, $this->getModelClass(), $matches);

if ([] === $matches) {
if (!isset($matches[1], $matches[3], $matches[5])) {
throw new \LogicException(sprintf(
'Please define a default `baseRoutePattern` value for the admin class `%s`',
static::class
Expand Down Expand Up @@ -1877,7 +1877,7 @@ protected function generateBaseRouteName(bool $isChildAdmin = false): string

preg_match(self::CLASS_REGEX, $this->getModelClass(), $matches);

if ([] === $matches) {
if (!isset($matches[1], $matches[3], $matches[5])) {
throw new \LogicException(sprintf(
'Cannot automatically determine base route name,'
.' please define a default `baseRouteName` value for the admin class `%s`',
Expand Down
60 changes: 49 additions & 11 deletions src/Security/Handler/RoleSecurityHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,8 @@ public function isGranted(AdminInterface $admin, $attributes, ?object $object =
$attributes = [$attributes];
}

// NEXT_MAJOR: Change the foreach to a single check.
$useAll = false;
foreach ($attributes as $pos => $attribute) {
// If the attribute is not already a ROLE_ we generate the related role.
if (\is_string($attribute) && !str_starts_with($attribute, 'ROLE_')) {
$attributes[$pos] = sprintf($this->getBaseRole($admin), $attribute);
// All the admin related role are available when you have the `_ALL` role.
$useAll = true;
}
}

$useAll = $this->hasOnlyAdminRoles($attributes);
$attributes = $this->mapAttributes($attributes, $admin);
$allRole = sprintf($this->getBaseRole($admin), 'ALL');

try {
Expand Down Expand Up @@ -125,4 +116,51 @@ private function isAnyGranted(array $attributes, ?object $subject = null): bool

return false;
}

/**
* @param array<string|Expression> $attributes
*/
private function hasOnlyAdminRoles(array $attributes): bool
{
// NEXT_MAJOR: Change the foreach to a single check.
foreach ($attributes as $attribute) {
// If the attribute is not already a ROLE_ we generate the related role.
if (\is_string($attribute) && !str_starts_with($attribute, 'ROLE_')) {
return true;
}
}

return false;
}

/**
* @param array<string|Expression> $attributes
* @param AdminInterface<object> $admin
*
* @return array<string|Expression>
*/
private function mapAttributes(array $attributes, AdminInterface $admin): array
{
$mappedAttributes = [];

foreach ($attributes as $attribute) {
if (!\is_string($attribute) || str_starts_with($attribute, 'ROLE_')) {
$mappedAttributes[] = $attribute;

continue;
}

$baseRole = $this->getBaseRole($admin);

$mappedAttributes[] = sprintf($baseRole, $attribute);

foreach ($admin->getSecurityInformation() as $role => $permissions) {
if (\in_array($attribute, $permissions, true)) {
$mappedAttributes[] = sprintf($baseRole, $role);
}
}
}

return array_unique($mappedAttributes);
}
}
48 changes: 48 additions & 0 deletions tests/Security/Handler/RoleSecurityHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,54 @@ public function provideGetBaseRoleCases(): iterable
yield ['ROLE_FOO_BAR_%s', 'FOO.BAR'];
}

/**
* @dataProvider provideIsGrantedWithSecurityInformationCases
*
* @param array<string, string[]> $informationMapping
* @param string[] $userRoles
*/
public function testIsGrantedWithSecurityInformation(array $informationMapping, array $userRoles, bool $expected): void
{
$handler = new RoleSecurityHandler($this->authorizationChecker, 'ROLE_SUPER_ADMIN');

$subject = new \stdClass();

$this->admin
->method('getCode')
->willReturn('test');
$this->admin->expects(static::once())
->method('getSecurityInformation')
->willReturn($informationMapping);

$this->authorizationChecker
->method('isGranted')
->willReturnCallback(static function (mixed $attribute, mixed $subject = null) use ($userRoles): bool {
if ($attribute instanceof Expression) {
$attribute = (string) $attribute;
}

if (\in_array($attribute, $userRoles, true)) {
return $subject instanceof \stdClass;
}

return false;
});

static::assertSame($expected, $handler->isGranted($this->admin, 'EDIT', $subject));
}

/**
* @phpstan-return iterable<array{array<string, string[]>, string[], bool}>
*/
public function provideIsGrantedWithSecurityInformationCases(): iterable
{
yield 'default mapping' => [[], ['ROLE_TEST_EDIT'], true];
yield 'with single mapping' => [['VIEW' => ['EDIT', 'SHOW']], ['ROLE_TEST_VIEW'], true];
yield 'with multiple mappings' => [['WRITE' => ['EDIT', 'SHOW'], 'MANAGE' => ['EDIT', 'SHOW', 'DELETE']], ['ROLE_TEST_MANAGE'], true];
yield 'with all mapping' => [['ADMIN' => ['ALL']], ['ROLE_TEST_ALL'], true];
yield 'with missing permission' => [['SHOW' => ['VIEW', 'SHOW']], ['ROLE_TEST_VIEW'], false];
}

/**
* NEXT_MAJOR: Remove the group legacy and only keep string $superAdminRoles and string|Expression $operation in dataProvider.
*
Expand Down

0 comments on commit 948053c

Please sign in to comment.