<?php
namespace App\Security;
use App\DBAL\Types\AccessRoleType;
use App\Entity;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class AccessVoter extends Voter
{
public const SYSTEM_ADMIN = 'SYSTEM_ADMIN';
public const DATA_EXPORT = 'DATA_EXPORT';
public const VIEW_REPORTS = 'VIEW_REPORTS';
public const VIEW_WIDGETS = 'VIEW_WIDGETS';
public const ACTION_CREATE = 'ACTION_CREATE';
public const ACTION_EDIT = 'ACTION_EDIT';
public const ACTION_DELETE = 'ACTION_DELETE';
public const VIEW_OPPORTUNITIES = 'VIEW_OPPORTUNITIES';
public const OBJECT_USER = 'OBJECT_USER';
public const OBJECT_CHAT = 'OBJECT_CHAT';
public const OBJECT_FILTER = 'OBJECT_FILTER';
public const OBJECT_LEAD = 'OBJECT_LEAD';
public const OBJECT_LEAD_COMMENT = 'OBJECT_LEAD_COMMENT';
public const OBJECT_CUSTOMER = 'OBJECT_CUSTOMER';
public const OBJECT_CUSTOMER_INTERVIEW_FORM = 'OBJECT_CUSTOMER_INTERVIEW_FORM';
public const OBJECT_CUSTOMER_INTERVIEW_NOTE = 'OBJECT_CUSTOMER_INTERVIEW_NOTE';
public const OBJECT_CUSTOMER_GOAL_TIME = 'OBJECT_CUSTOMER_GOAL_TIME';
public const OBJECT_CUSTOMER_INVOICE = 'OBJECT_CUSTOMER_INVOICE';
public const OBJECT_CUSTOMER_TRANSACTION = 'OBJECT_CUSTOMER_TRANSACTION';
public const OBJECT_CUSTOMER_MESSAGE = 'OBJECT_CUSTOMER_MESSAGE';
public const OBJECT_CUSTOMER_LETTER = 'OBJECT_CUSTOMER_LETTER';
public const OBJECT_CUSTOMER_FILE = 'OBJECT_CUSTOMER_FILE';
public const OBJECT_CUSTOMER_EVENT = 'OBJECT_CUSTOMER_EVENT';
public const OBJECT_CUSTOMER_EVENT_COMMENT = 'OBJECT_CUSTOMER_EVENT_COMMENT';
public const OBJECT_CUSTOMER_COMPLAINT = 'OBJECT_CUSTOMER_COMPLAINT';
public const OBJECT_CUSTOMER_COMPLAINT_COMMENT = 'OBJECT_CUSTOMER_COMPLAINT_COMMENT';
public const OBJECT_CUSTOMER_UPSELL_REQUEST = 'OBJECT_CUSTOMER_UPSELL_REQUEST';
public const OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT = 'OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT';
public const OBJECT_CUSTOMER_TAX_RETURN = 'OBJECT_CUSTOMER_TAX_RETURN';
public const OBJECT_CUSTOMER_TAX_RETURN_COMMENT = 'OBJECT_CUSTOMER_TAX_RETURN_COMMENT';
public const OBJECT_CUSTOMER_STATUS_LOG = 'OBJECT_CUSTOMER_STATUS_LOG';
public const OBJECT_CUSTOMER_RATING_LOG = 'OBJECT_CUSTOMER_RATING_LOG';
public const OBJECT_CUSTOMER_REQUEST = 'OBJECT_CUSTOMER_REQUEST';
public const OBJECT_EVENT = 'OBJECT_EVENT';
public const OBJECT_EVENT_COMMENT = 'OBJECT_EVENT_COMMENT';
public const OBJECT_PROJECT = 'OBJECT_PROJECT';
public const OBJECT_PROJECT_CARD = 'OBJECT_PROJECT_CARD';
public const OBJECT_PROJECT_ITEM = 'OBJECT_PROJECT_ITEM';
public const OBJECT_PROJECT_COMMENT = 'OBJECT_PROJECT_COMMENT';
public const OBJECT_PROJECT_WHITEBOARD = 'OBJECT_PROJECT_WHITEBOARD';
public const OBJECT_FEED = 'OBJECT_FEED';
public const OBJECT_FEED_OPTION = 'OBJECT_FEED_OPTION';
public const OBJECT_FEED_COMMENT = 'OBJECT_FEED_COMMENT';
public const OBJECT_ISSUE = 'OBJECT_ISSUE';
public const OBJECT_ISSUE_COMMENT = 'OBJECT_ISSUE_COMMENT';
public const OBJECT_STREAM = 'OBJECT_STREAM';
public const OBJECT_STREAM_COMMENT = 'OBJECT_STREAM_COMMENT';
public const OBJECT_MESSAGE = 'OBJECT_MESSAGE';
public const OBJECT_NOTIFICATION = 'OBJECT_NOTIFICATION';
public const OBJECT_REPORT = 'OBJECT_REPORT';
public const OBJECT_WIDGET = 'OBJECT_WIDGET';
/** @var AccessDecisionManagerInterface */
private $decisionManager;
/**
* @param AccessDecisionManagerInterface $decisionManager
*/
public function __construct(AccessDecisionManagerInterface $decisionManager)
{
$this->decisionManager = $decisionManager;
}
/**
* {@inheritdoc}
*/
protected function supports($attribute, $subject)
{
list($attribute) = explode('|', $attribute);
// if the attribute isn't one we support, return false
if (!\in_array($attribute, [
self::SYSTEM_ADMIN,
self::DATA_EXPORT,
self::VIEW_REPORTS,
self::VIEW_WIDGETS,
self::OBJECT_USER,
self::OBJECT_CHAT,
self::OBJECT_FILTER,
self::OBJECT_LEAD,
self::OBJECT_LEAD_COMMENT,
self::OBJECT_CUSTOMER,
self::OBJECT_CUSTOMER_INTERVIEW_FORM,
self::OBJECT_CUSTOMER_INTERVIEW_NOTE,
self::OBJECT_CUSTOMER_GOAL_TIME,
self::OBJECT_CUSTOMER_INVOICE,
self::OBJECT_CUSTOMER_TRANSACTION,
self::OBJECT_CUSTOMER_MESSAGE,
self::OBJECT_CUSTOMER_LETTER,
self::OBJECT_CUSTOMER_FILE,
self::OBJECT_CUSTOMER_EVENT,
self::OBJECT_CUSTOMER_EVENT_COMMENT,
self::OBJECT_CUSTOMER_UPSELL_REQUEST,
self::OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT,
self::OBJECT_CUSTOMER_TAX_RETURN,
self::OBJECT_CUSTOMER_TAX_RETURN_COMMENT,
self::OBJECT_CUSTOMER_COMPLAINT,
self::OBJECT_CUSTOMER_COMPLAINT_COMMENT,
self::OBJECT_CUSTOMER_STATUS_LOG,
self::OBJECT_CUSTOMER_RATING_LOG,
self::OBJECT_CUSTOMER_REQUEST,
self::OBJECT_EVENT,
self::OBJECT_EVENT_COMMENT,
self::OBJECT_PROJECT,
self::OBJECT_PROJECT_CARD,
self::OBJECT_PROJECT_ITEM,
self::OBJECT_PROJECT_COMMENT,
self::OBJECT_PROJECT_WHITEBOARD,
self::OBJECT_FEED,
self::OBJECT_FEED_OPTION,
self::OBJECT_FEED_COMMENT,
self::OBJECT_ISSUE,
self::OBJECT_ISSUE_COMMENT,
self::OBJECT_STREAM,
self::OBJECT_STREAM_COMMENT,
self::OBJECT_MESSAGE,
self::OBJECT_NOTIFICATION,
self::OBJECT_REPORT,
self::OBJECT_WIDGET,
])) {
return false;
}
// only vote on X objects inside this voter
if (!(!$subject
|| (self::OBJECT_USER === $attribute && $subject instanceof Entity\User)
|| (self::OBJECT_CHAT === $attribute && $subject instanceof Entity\Chat\Chat)
|| (self::OBJECT_FILTER === $attribute && $subject instanceof Entity\Filter)
|| (self::OBJECT_LEAD === $attribute && $subject instanceof Entity\Lead\Lead)
|| (self::OBJECT_LEAD_COMMENT === $attribute && $subject instanceof Entity\Lead\Comment)
|| (self::OBJECT_CUSTOMER === $attribute && $subject instanceof Entity\Customer\Customer)
|| (self::OBJECT_CUSTOMER_INTERVIEW_FORM === $attribute && $subject instanceof Entity\Customer\InterviewForm)
|| (self::OBJECT_CUSTOMER_INTERVIEW_NOTE === $attribute && $subject instanceof Entity\Customer\InterviewNote)
|| (self::OBJECT_CUSTOMER_GOAL_TIME === $attribute && $subject instanceof Entity\Customer\GoalTime)
|| (self::OBJECT_CUSTOMER_INVOICE === $attribute && $subject instanceof Entity\Customer\Invoice)
|| (self::OBJECT_CUSTOMER_TRANSACTION === $attribute && $subject instanceof Entity\Customer\Transaction)
|| (\in_array($attribute, [self::OBJECT_MESSAGE, self::OBJECT_CUSTOMER_MESSAGE]) && $subject instanceof Entity\Customer\Message)
|| (self::OBJECT_CUSTOMER_LETTER === $attribute && $subject instanceof Entity\Customer\Letter)
|| (self::OBJECT_CUSTOMER_FILE === $attribute && $subject instanceof Entity\Customer\File)
|| (self::OBJECT_CUSTOMER_COMPLAINT === $attribute && $subject instanceof Entity\Customer\Complaint)
|| (self::OBJECT_CUSTOMER_COMPLAINT_COMMENT === $attribute && $subject instanceof Entity\Customer\Comment\ComplaintComment)
|| (self::OBJECT_CUSTOMER_UPSELL_REQUEST === $attribute && $subject instanceof Entity\Customer\UpsellRequest)
|| (self::OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT === $attribute && $subject instanceof Entity\Customer\Comment\UpsellRequestComment)
|| (self::OBJECT_CUSTOMER_TAX_RETURN === $attribute && $subject instanceof Entity\Customer\TaxReturn)
|| (self::OBJECT_CUSTOMER_TAX_RETURN_COMMENT === $attribute && $subject instanceof Entity\Customer\Comment\TaxReturnComment)
|| (self::OBJECT_CUSTOMER_STATUS_LOG === $attribute && $subject instanceof Entity\Customer\StatusLog)
|| (self::OBJECT_CUSTOMER_RATING_LOG === $attribute && $subject instanceof Entity\Customer\RatingLog)
|| (self::OBJECT_CUSTOMER_REQUEST === $attribute && $subject instanceof Entity\Customer\Request)
|| (\in_array($attribute, [self::OBJECT_EVENT, self::OBJECT_CUSTOMER_EVENT]) && $subject instanceof Entity\Event\Event)
|| (\in_array($attribute, [self::OBJECT_EVENT_COMMENT, self::OBJECT_CUSTOMER_EVENT_COMMENT]) && $subject instanceof Entity\Event\Comment)
|| (self::OBJECT_PROJECT === $attribute && $subject instanceof Entity\Project\Project)
|| (self::OBJECT_PROJECT_CARD === $attribute && $subject instanceof Entity\Project\Card)
|| (self::OBJECT_PROJECT_ITEM === $attribute && $subject instanceof Entity\Project\Item)
|| (self::OBJECT_PROJECT_COMMENT === $attribute && $subject instanceof Entity\Project\Comment)
|| (self::OBJECT_PROJECT_WHITEBOARD === $attribute && $subject instanceof Entity\Project\Whiteboard)
|| (self::OBJECT_FEED === $attribute && $subject instanceof Entity\Feed\Feed)
|| (self::OBJECT_FEED_OPTION === $attribute && $subject instanceof Entity\Feed\Option)
|| (self::OBJECT_FEED_COMMENT === $attribute && $subject instanceof Entity\Feed\Comment)
|| (self::OBJECT_ISSUE === $attribute && $subject instanceof Entity\Issue\Issue)
|| (self::OBJECT_ISSUE_COMMENT === $attribute && $subject instanceof Entity\Issue\Comment)
|| (self::OBJECT_STREAM === $attribute && $subject instanceof Entity\Stream\Stream)
|| (self::OBJECT_STREAM_COMMENT === $attribute && $subject instanceof Entity\Stream\Comment)
|| (self::OBJECT_NOTIFICATION === $attribute && $subject instanceof Entity\Notification)
|| (self::OBJECT_REPORT === $attribute && $subject instanceof Entity\Report)
|| (self::OBJECT_WIDGET === $attribute && $subject instanceof Entity\Widget)
)) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$tmp = explode('|', $attribute);
$attribute = $tmp[0];
$action = $tmp[1] ?? null;
switch ($action) {
case self::ACTION_CREATE:
$action = AccessRoleType::CREATE;
break;
case self::ACTION_EDIT:
$action = AccessRoleType::EDIT;
break;
case self::ACTION_DELETE:
$action = AccessRoleType::DELETE;
break;
case self::VIEW_OPPORTUNITIES:
break;
default:
$action = null;
}
$user = $token->getUser();
// the user must be logged in; if not, deny access
if (!$user instanceof Entity\User) {
return false;
}
// ROLE_ADMIN can do anything!
if ($this->decisionManager->decide($token, ['ROLE_ADMIN']) || $user->isAllowSystemAdmin()) {
return true;
}
if (self::DATA_EXPORT === $attribute && $user->isAllowDataExport()) {
return true;
}
if (self::VIEW_REPORTS === $attribute && $user->hasAccessToReport()) {
return true;
}
if (self::VIEW_WIDGETS === $attribute/* && $user->hasAccessToWidget() */) {
return true;
}
// you can only perform self actions
if (self::OBJECT_USER === $attribute
&& $subject instanceof User
&& $subject->getId() === $user->getId()
) {
return true;
}
// check access via team
if (!$subject
&& \in_array($attribute, [
self::OBJECT_LEAD,
self::OBJECT_CUSTOMER,
self::OBJECT_EVENT,
self::OBJECT_PROJECT,
self::OBJECT_FEED,
self::OBJECT_ISSUE,
self::OBJECT_STREAM,
self::OBJECT_MESSAGE,
])
&& $user->hasEntityAccess(substr(strtolower($attribute), 7), $action)
) {
return true;
}
if (!$subject
&& \in_array($attribute, [
self::OBJECT_CUSTOMER_INTERVIEW_FORM,
self::OBJECT_CUSTOMER_INTERVIEW_NOTE,
self::OBJECT_CUSTOMER_GOAL_TIME,
self::OBJECT_CUSTOMER_INVOICE,
self::OBJECT_CUSTOMER_TRANSACTION,
self::OBJECT_CUSTOMER_MESSAGE,
self::OBJECT_CUSTOMER_LETTER,
self::OBJECT_CUSTOMER_FILE,
self::OBJECT_CUSTOMER_EVENT,
self::OBJECT_CUSTOMER_EVENT_COMMENT,
self::OBJECT_CUSTOMER_UPSELL_REQUEST,
self::OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT,
self::OBJECT_CUSTOMER_TAX_RETURN,
self::OBJECT_CUSTOMER_TAX_RETURN_COMMENT,
self::OBJECT_CUSTOMER_COMPLAINT,
self::OBJECT_CUSTOMER_COMPLAINT_COMMENT,
self::OBJECT_CUSTOMER_STATUS_LOG,
self::OBJECT_CUSTOMER_RATING_LOG,
self::OBJECT_CUSTOMER_REQUEST,
])
&& $user->hasEntityAccess(substr(strtolower(self::OBJECT_CUSTOMER), 7), AccessRoleType::EDIT)
) {
return true;
}
if ($subject) {
// reject subjects connected to other users
if (\in_array($attribute, [
self::OBJECT_FILTER,
self::OBJECT_NOTIFICATION,
]) && (!$subject->getUser()
|| $subject->getUser()->getId() !== $user->getId()
)) {
return false;
}
// allow non-configured permission subjects
if (\in_array($attribute, [
self::OBJECT_FILTER,
self::OBJECT_NOTIFICATION,
])) {
return true;
}
// access to chat
if (self::OBJECT_CHAT === $attribute) {
return $subject->hasParticipantByUser($user);
}
// access to reports
if (self::OBJECT_REPORT === $attribute) {
return $user->hasAccessToReport($subject);
}
// access to widgets
if (self::OBJECT_WIDGET === $attribute) {
return $user->hasAccessToWidget($subject);
}
// no user asigned
if (!method_exists($subject, 'getUser') || !$creator = $subject->getUser()) {
return true;
}
/*
// creator is me
if ($user->getId() === $creator->getId()) {
return true;
}
*/
if (\in_array($attribute, [
self::OBJECT_LEAD_COMMENT,
self::OBJECT_EVENT_COMMENT,
self::OBJECT_PROJECT_COMMENT,
self::OBJECT_FEED_COMMENT,
self::OBJECT_ISSUE_COMMENT,
self::OBJECT_STREAM_COMMENT,
])) {
$attribute = substr($attribute, 0, -8);
}
if (\in_array($attribute, [
self::OBJECT_PROJECT_CARD,
self::OBJECT_PROJECT_ITEM,
])) {
$attribute = substr($attribute, 0, -5);
}
if (\in_array($attribute, [
self::OBJECT_PROJECT_WHITEBOARD,
])) {
$attribute = substr($attribute, 0, -12);
}
if (\in_array($attribute, [
self::OBJECT_FEED_OPTION,
])) {
$attribute = substr($attribute, 0, -7);
}
if (\in_array($attribute, [
self::OBJECT_CUSTOMER_INTERVIEW_FORM,
self::OBJECT_CUSTOMER_INTERVIEW_NOTE,
self::OBJECT_CUSTOMER_GOAL_TIME,
self::OBJECT_CUSTOMER_INVOICE,
self::OBJECT_CUSTOMER_TRANSACTION,
self::OBJECT_CUSTOMER_MESSAGE,
self::OBJECT_CUSTOMER_LETTER,
self::OBJECT_CUSTOMER_FILE,
self::OBJECT_CUSTOMER_EVENT,
self::OBJECT_CUSTOMER_EVENT_COMMENT,
self::OBJECT_CUSTOMER_UPSELL_REQUEST,
self::OBJECT_CUSTOMER_UPSELL_REQUEST_COMMENT,
self::OBJECT_CUSTOMER_TAX_RETURN,
self::OBJECT_CUSTOMER_TAX_RETURN_COMMENT,
self::OBJECT_CUSTOMER_COMPLAINT,
self::OBJECT_CUSTOMER_COMPLAINT_COMMENT,
self::OBJECT_CUSTOMER_STATUS_LOG,
self::OBJECT_CUSTOMER_RATING_LOG,
self::OBJECT_CUSTOMER_REQUEST,
])) {
$attribute = self::OBJECT_CUSTOMER;
if (AccessRoleType::DELETE === $action) {
$action = AccessRoleType::EDIT;
}
}
// access to record
if ($user->hasEntityAccess(substr(strtolower($attribute), 7), $action)) {
return true;
}
}
return false;
}
}