Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)

This commit is contained in:
Kevin Papst
2022-12-31 21:19:55 +01:00
committed by GitHub
parent 95e06746bd
commit 90a0fd8a22
2164 changed files with 83700 additions and 86426 deletions

View File

@@ -1,83 +0,0 @@
<?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\Voter;
use App\Entity\User;
use App\Security\AclDecisionManager;
use App\Security\RolePermissionManager;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
* Abstract voter to help with checking user permissions.
* @codeCoverageIgnore
* @deprecated since 1.13
*/
abstract class AbstractVoter extends Voter
{
/**
* @var AclDecisionManager
*/
protected $decisionManager;
/**
* @var RolePermissionManager
*/
protected $roleManager;
public function __construct(AclDecisionManager $decisionManager, RolePermissionManager $roleManager)
{
$this->decisionManager = $decisionManager;
$this->roleManager = $roleManager;
}
/**
* @param TokenInterface $token
* @return bool
*/
protected function isFullyAuthenticated(TokenInterface $token)
{
return $this->decisionManager->isFullyAuthenticated($token);
}
/**
* @param string $role
* @param string $permission
* @return bool
*/
protected function hasPermission($role, $permission)
{
return $this->roleManager->hasPermission($role, $permission);
}
/**
* @param User $user
* @param string $permission
* @return bool
*/
protected function hasRolePermission(User $user, $permission)
{
foreach ($user->getRoles() as $role) {
if ($this->hasPermission($role, $permission)) {
return true;
}
}
return false;
}
/**
* @param string $permission
* @return bool
*/
public function isRegisteredPermission($permission)
{
return $this->roleManager->isRegisteredPermission($permission);
}
}

View File

@@ -33,11 +33,8 @@ final class ActivityVoter extends Voter
'permissions',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -45,7 +42,7 @@ final class ActivityVoter extends Voter
* @param Activity $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof Activity)) {
return false;
@@ -64,7 +61,7 @@ final class ActivityVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -33,16 +33,12 @@ final class CustomerVoter extends Voter
'delete',
'permissions',
'comments',
'comments_create',
'details',
'access',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -50,7 +46,7 @@ final class CustomerVoter extends Voter
* @param Customer $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof Customer)) {
return false;
@@ -69,7 +65,7 @@ final class CustomerVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -34,11 +34,8 @@ final class EntityMultiRoleVoter extends Voter
'activity',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -46,7 +43,7 @@ final class EntityMultiRoleVoter extends Voter
* @param Activity|Project|Customer|string $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) {
return false;
@@ -69,7 +66,7 @@ final class EntityMultiRoleVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -32,15 +32,11 @@ final class ProjectVoter extends Voter
'delete',
'permissions',
'comments',
'comments_create',
'details',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -48,7 +44,7 @@ final class ProjectVoter extends Voter
* @param Project $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof Project)) {
return false;
@@ -67,7 +63,7 @@ final class ProjectVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -17,13 +17,8 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
final class QuickEntryVoter extends Voter
{
private $permissionManager;
private $trackingModeService;
public function __construct(RolePermissionManager $permissionManager, TrackingModeService $trackingModeService)
public function __construct(private RolePermissionManager $permissionManager, private TrackingModeService $trackingModeService)
{
$this->permissionManager = $permissionManager;
$this->trackingModeService = $trackingModeService;
}
/**
@@ -31,7 +26,7 @@ final class QuickEntryVoter extends Voter
* @param mixed $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
return 'quick-entry' === $attribute;
}
@@ -42,7 +37,7 @@ final class QuickEntryVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -0,0 +1,83 @@
<?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\Voter;
use App\Entity\User;
use App\Security\RolePermissionManager;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
final class ReportingVoter extends Voter
{
private const ALLOWED_ATTRIBUTES = [
'report:customer',
'report:other',
'report:project',
'report:user',
];
public function __construct(private RolePermissionManager $permissionManager)
{
}
/**
* @param string $attribute
* @param null $subject
* @return bool
*/
protected function supports(string $attribute, mixed $subject): bool
{
return $subject === null && \in_array($attribute, self::ALLOWED_ATTRIBUTES);
}
/**
* @param string $attribute
* @param null $subject
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
$permissions = ['view_reporting'];
switch ($attribute) {
case 'report:customer':
$permissions[] = 'customer_reporting';
break;
case 'report:other':
$permissions[] = 'view_other_reporting';
$permissions[] = 'view_other_timesheet';
break;
case 'report:project':
$permissions[] = 'project_reporting';
break;
case 'report:user':
// own reports are always allowed if reporting can be accessed
break;
}
foreach ($permissions as $permission) {
if (!$this->permissionManager->hasRolePermission($user, $permission)) {
return false;
}
}
return true;
}
}

View File

@@ -20,11 +20,8 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
*/
final class RolePermissionVoter extends Voter
{
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -32,7 +29,7 @@ final class RolePermissionVoter extends Voter
* @param mixed $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
// we only work on single strings that have no subject
if (null !== $subject) {
@@ -48,7 +45,7 @@ final class RolePermissionVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -26,11 +26,8 @@ final class TeamVoter extends Voter
'delete',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -38,7 +35,7 @@ final class TeamVoter extends Voter
* @param Team $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof Team)) {
return false;
@@ -57,7 +54,7 @@ final class TeamVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();

View File

@@ -48,18 +48,13 @@ final class TimesheetVoter extends Voter
'duplicate'
];
private $permissionManager;
private $lockdownService;
private ?bool $lockdownGrace = null;
private ?bool $lockdownOverride = null;
private ?bool $editExported = null;
private ?\DateTime $now = null;
private $lockdownGrace;
private $lockdownOverride;
private $editExported;
private $now;
public function __construct(RolePermissionManager $permissionManager, LockdownService $lockdownService)
public function __construct(private RolePermissionManager $permissionManager, private LockdownService $lockdownService)
{
$this->permissionManager = $permissionManager;
$this->lockdownService = $lockdownService;
}
/**
@@ -67,7 +62,7 @@ final class TimesheetVoter extends Voter
* @param mixed $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof Timesheet)) {
return false;
@@ -86,7 +81,7 @@ final class TimesheetVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
@@ -142,7 +137,7 @@ final class TimesheetVoter extends Voter
$permission .= '_';
// extend me for "team" support later on
if ($subject->getUser()->getId() == $user->getId()) {
if ($subject->getUser()->getId() === $user->getId()) {
$permission .= 'own';
} else {
$permission .= 'other';
@@ -153,7 +148,7 @@ final class TimesheetVoter extends Voter
return $this->permissionManager->hasRolePermission($user, $permission);
}
protected function canStart(Timesheet $timesheet): bool
private function canStart(Timesheet $timesheet): bool
{
// possible improvements for the future:
// we could check the amount of active entries (maybe slow)
@@ -167,7 +162,7 @@ final class TimesheetVoter extends Voter
return false;
}
if (!$timesheet->getActivity()->isVisible() || !$timesheet->getProject()->isVisible()) {
if (!$timesheet->getProject()->isVisible()) {
return false;
}
@@ -175,10 +170,14 @@ final class TimesheetVoter extends Voter
return false;
}
if (!$timesheet->getActivity()->isVisible()) {
return false;
}
return true;
}
protected function canEdit(User $user, Timesheet $timesheet): bool
private function canEdit(User $user, Timesheet $timesheet): bool
{
if (!$this->isAllowedExported($user, $timesheet)) {
return false;
@@ -191,7 +190,7 @@ final class TimesheetVoter extends Voter
return true;
}
protected function canDelete(User $user, Timesheet $timesheet): bool
private function canDelete(User $user, Timesheet $timesheet): bool
{
if (!$this->isAllowedExported($user, $timesheet)) {
return false;
@@ -204,7 +203,7 @@ final class TimesheetVoter extends Voter
return true;
}
protected function canDuplicate(User $user, Timesheet $timesheet): bool
private function canDuplicate(User $user, Timesheet $timesheet): bool
{
if (!$this->isAllowedInLockdown($user, $timesheet)) {
return false;

View File

@@ -25,6 +25,7 @@ final class UserVoter extends Voter
'roles',
'teams',
'password',
'2fa',
'delete',
'preferences',
'api-token',
@@ -32,11 +33,8 @@ final class UserVoter extends Voter
'view_team_member',
];
private $permissionManager;
public function __construct(RolePermissionManager $permissionManager)
public function __construct(private RolePermissionManager $permissionManager)
{
$this->permissionManager = $permissionManager;
}
/**
@@ -44,7 +42,7 @@ final class UserVoter extends Voter
* @param mixed $subject
* @return bool
*/
protected function supports($attribute, $subject)
protected function supports(string $attribute, mixed $subject): bool
{
if (!($subject instanceof User)) {
return false;
@@ -63,7 +61,7 @@ final class UserVoter extends Voter
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
@@ -93,6 +91,12 @@ final class UserVoter extends Voter
}
}
if ($attribute === '2fa') {
// two factor only works for internal users and
// can only be activated by the logged-in user for himself
return $subject->isInternalUser() && $subject->getId() === $user->getId();
}
$permission = $attribute;
// extend me for "team" support later on