Re-usable ACL checks on teams (#5925)
This commit is contained in:
@@ -13,6 +13,7 @@ use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Team;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\User\PermissionService;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
@@ -115,15 +116,15 @@ final class RolePermissionManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Team> $teams
|
||||
* @param array<int, Team>|Collection<int, Team> $teams
|
||||
*/
|
||||
private function checkTeamAccess(Collection $teams, User $user): bool
|
||||
private function checkTeamAccess(Collection|array $teams, User $user): bool
|
||||
{
|
||||
if ($user->canSeeAllData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($teams->count() === 0) {
|
||||
if (\count($teams) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,6 +137,28 @@ final class RolePermissionManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, Team>|Collection<int, Team> $teams
|
||||
*/
|
||||
private function checkTeamLeadAccess(Collection|array $teams, User $user): bool
|
||||
{
|
||||
if ($user->canSeeAllData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\count($teams) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($teams as $team) {
|
||||
if ($user->isTeamleadOf($team)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function checkTeamAccessCustomer(Customer $customer, User $user): bool
|
||||
{
|
||||
return $this->checkTeamAccess($customer->getTeams(), $user);
|
||||
@@ -158,4 +181,21 @@ final class RolePermissionManager
|
||||
|
||||
return $this->checkTeamAccess($activity->getTeams(), $user);
|
||||
}
|
||||
|
||||
public function checkTeamAccessTimesheet(Timesheet $timesheet, User $user): bool
|
||||
{
|
||||
if ($user->getId() !== null && $user->getId() === $timesheet->getUser()?->getId()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($timesheet->getProject() !== null && !$this->checkTeamAccessProject($timesheet->getProject(), $user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($timesheet->getActivity() !== null && !$this->checkTeamAccessActivity($timesheet->getActivity(), $user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->checkTeamLeadAccess($timesheet->getUser()?->getTeams() ?? [], $user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
namespace App\Voter;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Team;
|
||||
use App\Entity\User;
|
||||
use App\Security\RolePermissionManager;
|
||||
@@ -57,37 +55,6 @@ final class ActivityVoter extends Voter
|
||||
return $subject instanceof Activity && $this->supportsAttribute($attribute);
|
||||
}
|
||||
|
||||
private function checkTeamPermission(Activity|Project|Customer $subject, User $user): bool
|
||||
{
|
||||
if ($user->canSeeAllData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($subject instanceof Activity && $subject->getProject() !== null) {
|
||||
if (!$this->checkTeamPermission($subject->getProject(), $user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subject instanceof Project && $subject->getCustomer() !== null) {
|
||||
if (!$this->checkTeamPermission($subject->getCustomer(), $user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subject->getTeams()->count() === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($subject->getTeams() as $team) {
|
||||
if ($user->isInTeam($team)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
@@ -99,7 +66,7 @@ final class ActivityVoter extends Voter
|
||||
// this is a virtual permission, only meant to be used by developer
|
||||
// it checks if access to the given activity is potentially possible
|
||||
if ($attribute === 'access') {
|
||||
return $this->checkTeamPermission($subject, $user);
|
||||
return $this->permissionManager->checkTeamAccessActivity($subject, $user);
|
||||
}
|
||||
|
||||
if ($this->permissionManager->hasRolePermission($user, $attribute . '_activity')) {
|
||||
|
||||
@@ -69,21 +69,7 @@ final class CustomerVoter extends Voter
|
||||
// this is a virtual permission, only meant to be used by developer
|
||||
// it checks if access to the given customer is potentially possible
|
||||
if ($attribute === 'access') {
|
||||
if ($subject->getTeams()->count() === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($subject->getTeams() as $team) {
|
||||
if ($user->isInTeam($team)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($user->canSeeAllData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->permissionManager->checkTeamAccessCustomer($subject, $user);
|
||||
}
|
||||
|
||||
if ($this->permissionManager->hasRolePermission($user, $attribute . '_customer')) {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace App\Voter;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Team;
|
||||
use App\Entity\User;
|
||||
@@ -58,31 +57,6 @@ final class ProjectVoter extends Voter
|
||||
return $subject instanceof Project && $this->supportsAttribute($attribute);
|
||||
}
|
||||
|
||||
private function checkTeamPermission(Project|Customer $subject, User $user): bool
|
||||
{
|
||||
if ($user->canSeeAllData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($subject instanceof Project && $subject->getCustomer() !== null) {
|
||||
if (!$this->checkTeamPermission($subject->getCustomer(), $user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subject->getTeams()->count() === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($subject->getTeams() as $team) {
|
||||
if ($user->isInTeam($team)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
@@ -94,7 +68,7 @@ final class ProjectVoter extends Voter
|
||||
// this is a virtual permission, only meant to be used by developer
|
||||
// it checks if access to the given project is potentially possible
|
||||
if ($attribute === 'access') {
|
||||
return $this->checkTeamPermission($subject, $user);
|
||||
return $this->permissionManager->checkTeamAccessProject($subject, $user);
|
||||
}
|
||||
|
||||
if ($this->permissionManager->hasRolePermission($user, $attribute . '_project')) {
|
||||
|
||||
@@ -131,18 +131,15 @@ final class TimesheetVoter extends Voter
|
||||
return false;
|
||||
}
|
||||
|
||||
$permission .= '_';
|
||||
|
||||
// extend me for "team" support later on
|
||||
if ($subject->getUser()?->getId() === $user->getId()) {
|
||||
$permission .= 'own';
|
||||
} else {
|
||||
$permission .= 'other';
|
||||
return $this->permissionManager->hasRolePermission($user, $permission . '_own_timesheet');
|
||||
}
|
||||
|
||||
$permission .= '_timesheet';
|
||||
if (!$this->permissionManager->checkTeamAccessTimesheet($subject, $user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->permissionManager->hasRolePermission($user, $permission);
|
||||
return $this->permissionManager->hasRolePermission($user, $permission . '_other_timesheet');
|
||||
}
|
||||
|
||||
private function canStart(Timesheet $timesheet): bool
|
||||
|
||||
Reference in New Issue
Block a user