added permission caching (#3877)

This commit is contained in:
Kevin Papst
2023-02-25 20:57:48 +01:00
committed by GitHub
parent 3c28288cac
commit a8b972f8a5
7 changed files with 90 additions and 24 deletions

View File

@@ -16,11 +16,11 @@ use App\Event\PermissionSectionsEvent;
use App\Event\PermissionsEvent;
use App\Form\RoleType;
use App\Model\PermissionSection;
use App\Repository\RolePermissionRepository;
use App\Repository\RoleRepository;
use App\Repository\UserRepository;
use App\Security\RolePermissionManager;
use App\Security\RoleService;
use App\User\PermissionService;
use App\Utils\PageSetup;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -227,7 +227,7 @@ final class PermissionController extends AbstractController
#[Route(path: '/roles/{id}/{name}/{value}/{csrfToken}', name: 'admin_user_permission_save', methods: ['POST'])]
#[IsGranted('role_permissions')]
public function savePermission(Role $role, string $name, bool $value, string $csrfToken, RolePermissionRepository $rolePermissionRepository, CsrfTokenManagerInterface $csrfTokenManager): Response
public function savePermission(Role $role, string $name, bool $value, string $csrfToken, PermissionService $permissionService, CsrfTokenManagerInterface $csrfTokenManager): Response
{
if (!$this->isCsrfTokenValid(self::TOKEN_NAME, $csrfToken)) {
throw new BadRequestHttpException('Invalid CSRF token');
@@ -242,7 +242,7 @@ final class PermissionController extends AbstractController
}
try {
$permission = $rolePermissionRepository->findRolePermission($role, $name);
$permission = $permissionService->findRolePermission($role, $name);
if (null === $permission) {
$permission = new RolePermission();
$permission->setRole($role);
@@ -250,7 +250,7 @@ final class PermissionController extends AbstractController
}
$permission->setAllowed($value);
$rolePermissionRepository->saveRolePermission($permission);
$permissionService->saveRolePermission($permission);
// refreshToken instead of getToken for more security but worse UX
// fast clicking with slow response times would fail, as the token cannot be replaced fast enough

View File

@@ -18,7 +18,7 @@ use Doctrine\ORM\EntityRepository;
*/
class RolePermissionRepository extends EntityRepository
{
public function saveRolePermission(RolePermission $permission)
public function saveRolePermission(RolePermission $permission): void
{
$entityManager = $this->getEntityManager();
$entityManager->persist($permission);

View File

@@ -10,7 +10,7 @@
namespace App\Security;
use App\Entity\User;
use App\Repository\RolePermissionRepository;
use App\User\PermissionService;
final class RolePermissionManager
{
@@ -29,11 +29,11 @@ final class RolePermissionManager
private bool $isInitialized = false;
/**
* @param RolePermissionRepository $repository
* @param PermissionService $service
* @param array<string, array<string, bool>> $permissions as defined in kimai.yaml
* @param array<string, bool> $permissionNames as defined in kimai.yaml
*/
public function __construct(private RolePermissionRepository $repository, private array $permissions, private array $permissionNames)
public function __construct(private PermissionService $service, private array $permissions, private array $permissionNames)
{
}
@@ -43,7 +43,7 @@ final class RolePermissionManager
return;
}
foreach ($this->repository->getAllAsArray() as $item) {
foreach ($this->service->getPermissions() as $item) {
$perm = (string) $item['permission'];
$role = (string) $item['role'];

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\User;
use App\Entity\Role;
use App\Entity\RolePermission;
use App\Repository\RolePermissionRepository;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
/**
* Caches permissions, which rarely change once Kimai is setup.
*
* @final
*/
class PermissionService
{
/**
* @var null|array<int, array<string, string|bool>>
*/
private ?array $cacheAll = null;
public function __construct(
private RolePermissionRepository $repository,
private CacheInterface $cache
) {
}
public function saveRolePermission(RolePermission $permission): void
{
$this->repository->saveRolePermission($permission);
$this->cache->delete('permissions');
}
public function findRolePermission(Role $role, string $permission): ?RolePermission
{
return $this->repository->findRolePermission($role, $permission);
}
/**
* @return array<int, array<string, string|bool>>
*/
public function getPermissions(): array
{
if ($this->cacheAll === null) {
$this->cacheAll = $this->cache->get('permissions', function (ItemInterface $item) {
$item->expiresAfter(86400); // one day
return $this->repository->getAllAsArray();
});
}
return $this->cacheAll;
}
}