mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-20 17:15:51 +02:00
Check permissions for time travel and element undo.
This commit is contained in:
parent
254d4e6c69
commit
8a61b465d0
23 changed files with 370 additions and 90 deletions
|
@ -320,6 +320,10 @@ showing the sidebar (on devices with md or higher)
|
|||
}
|
||||
}
|
||||
|
||||
.not-allowed {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
btn-xs
|
||||
btn-xs
|
||||
|
|
|
@ -67,6 +67,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
show_history:
|
||||
label: "perm.part.show_history"
|
||||
bit: 30
|
||||
revert_element:
|
||||
label: "perm.revert_elements"
|
||||
bit: 32
|
||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||
|
||||
parts_name: &PART_ATTRIBUTE # We define a template here, that we can use for all part attributes.
|
||||
label: "perm.part.name"
|
||||
|
@ -154,8 +158,31 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
label: "perm.part.lots"
|
||||
|
||||
parts_attachments:
|
||||
<<: *PART_MULTI_ATTRIBUTE
|
||||
group: "structures"
|
||||
label: "perm.part.attachments"
|
||||
operations:
|
||||
read:
|
||||
label: "perm.read"
|
||||
bit: 0
|
||||
edit:
|
||||
label: "perm.edit"
|
||||
bit: 2
|
||||
alsoSet: 'read'
|
||||
create:
|
||||
label: "perm.create"
|
||||
bit: 4
|
||||
alsoSet: ['read', 'edit']
|
||||
delete:
|
||||
label: "perm.delete"
|
||||
bit: 6
|
||||
alsoSet: ['read']
|
||||
show_history:
|
||||
label: "perm.show_history"
|
||||
bit: 8
|
||||
revert_element:
|
||||
label: "perm.revert_elements"
|
||||
bit: 10
|
||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||
|
||||
parts_order:
|
||||
<<: *PART_ATTRIBUTE
|
||||
|
@ -189,6 +216,13 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
show_users:
|
||||
label: "perm.show_users"
|
||||
bit: 12
|
||||
show_history:
|
||||
label: "perm.show_history"
|
||||
bit: 14
|
||||
revert_element:
|
||||
label: "perm.revert_elements"
|
||||
bit: 16
|
||||
alsoSet: ["read", "edit", "create", "delete", "show_history"]
|
||||
|
||||
footprints:
|
||||
<<: *PART_CONTAINING
|
||||
|
@ -243,6 +277,12 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
statistics:
|
||||
label: "perm.tools.statistics"
|
||||
bit: 10
|
||||
lastActivity:
|
||||
label: "perm.tools.lastActivity"
|
||||
bit: 12
|
||||
timetravel:
|
||||
label: "perm.tools.timeTravel"
|
||||
bit: 14
|
||||
|
||||
groups:
|
||||
label: "perm.groups"
|
||||
|
@ -270,6 +310,13 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
label: "perm.edit_permissions"
|
||||
alsoSet: ['read', 'edit']
|
||||
bit: 10
|
||||
show_history:
|
||||
label: "perm.show_history"
|
||||
bit: 12
|
||||
revert_element:
|
||||
label: "perm.revert_elements"
|
||||
bit: 14
|
||||
alsoSet: ["read", "edit", "create", "delete", "move", "edit_permissions", "show_history"]
|
||||
|
||||
users:
|
||||
label: "perm.users"
|
||||
|
@ -309,6 +356,13 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
|||
change_user_settings:
|
||||
label: "perm.users.change_user_settings"
|
||||
bit: 16
|
||||
show_history:
|
||||
label: "perm.show_history"
|
||||
bit: 18
|
||||
revert_element:
|
||||
label: "perm.revert_elements"
|
||||
bit: 20
|
||||
alsoSet: ["read", "edit", "create", "delete", "move", "edit_permissions", "show_history", "edit_infos", "change_group", "edit_username"]
|
||||
|
||||
database:
|
||||
label: "perm.database"
|
||||
|
|
|
@ -89,9 +89,9 @@ abstract class BaseAdminController extends AbstractController
|
|||
protected $dataTableFactory;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, UserPasswordEncoderInterface $passwordEncoder,
|
||||
AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler,
|
||||
EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel,
|
||||
DataTableFactory $dataTableFactory)
|
||||
AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler,
|
||||
EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel,
|
||||
DataTableFactory $dataTableFactory)
|
||||
{
|
||||
if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) {
|
||||
throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!');
|
||||
|
@ -119,6 +119,8 @@ abstract class BaseAdminController extends AbstractController
|
|||
|
||||
$timeTravel_timestamp = null;
|
||||
if ($timestamp !== null) {
|
||||
$this->denyAccessUnlessGranted('@tools.timeTravel');
|
||||
$this->denyAccessUnlessGranted('show_history', $part);
|
||||
//If the timestamp only contains numbers interpret it as unix timestamp
|
||||
if (ctype_digit($timestamp)) {
|
||||
$timeTravel_timestamp = new \DateTime();
|
||||
|
@ -129,14 +131,22 @@ abstract class BaseAdminController extends AbstractController
|
|||
$this->timeTravel->revertEntityToTimestamp($entity, $timeTravel_timestamp);
|
||||
}
|
||||
|
||||
$table = $this->dataTableFactory->createFromType(LogDataTable::class, [
|
||||
'filter_elements' => $this->historyHelper->getAssociatedElements($entity),
|
||||
'mode' => 'element_history'
|
||||
], ['pageLength' => 10])
|
||||
->handleRequest($request);
|
||||
if ($this->isGranted('show_history', $entity) ) {
|
||||
$table = $this->dataTableFactory->createFromType(
|
||||
LogDataTable::class,
|
||||
[
|
||||
'filter_elements' => $this->historyHelper->getAssociatedElements($entity),
|
||||
'mode' => 'element_history'
|
||||
],
|
||||
['pageLength' => 10]
|
||||
)
|
||||
->handleRequest($request);
|
||||
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
}
|
||||
} else {
|
||||
$table = null;
|
||||
}
|
||||
|
||||
$form = $this->createForm($this->form_class, $entity, [
|
||||
|
|
|
@ -86,13 +86,21 @@ class HomepageController extends AbstractController
|
|||
*/
|
||||
public function homepage(Request $request, GitVersionInfo $versionInfo): Response
|
||||
{
|
||||
$table = $this->dataTable->createFromType(LogDataTable::class, [
|
||||
'mode' => 'last_activity'
|
||||
], ['pageLength' => 10])
|
||||
->handleRequest($request);
|
||||
if ($this->isGranted("@tools.lastActivity")) {
|
||||
$table = $this->dataTable->createFromType(
|
||||
LogDataTable::class,
|
||||
[
|
||||
'mode' => 'last_activity'
|
||||
],
|
||||
['pageLength' => 10]
|
||||
)
|
||||
->handleRequest($request);
|
||||
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
}
|
||||
} else {
|
||||
$table = null;
|
||||
}
|
||||
|
||||
return $this->render('homepage.html.twig', [
|
||||
|
|
|
@ -121,6 +121,8 @@ class LogController extends AbstractController
|
|||
throw new \InvalidArgumentException('No log entry with the given ID is existing!');
|
||||
}
|
||||
|
||||
$this->denyAccessUnlessGranted('revert_element', $log_element->getTargetClass());
|
||||
|
||||
$eventUndoHelper->setMode($mode);
|
||||
$eventUndoHelper->setUndoneEvent($log_element);
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ class PartController extends AbstractController
|
|||
|
||||
$timeTravel_timestamp = null;
|
||||
if ($timestamp !== null) {
|
||||
$this->denyAccessUnlessGranted('@tools.timeTravel');
|
||||
$this->denyAccessUnlessGranted('show_history', $part);
|
||||
//If the timestamp only contains numbers interpret it as unix timestamp
|
||||
if (ctype_digit($timestamp)) {
|
||||
$timeTravel_timestamp = new \DateTime();
|
||||
|
@ -108,14 +110,18 @@ class PartController extends AbstractController
|
|||
$timeTravel->revertEntityToTimestamp($part, $timeTravel_timestamp);
|
||||
}
|
||||
|
||||
$table = $dataTable->createFromType(LogDataTable::class, [
|
||||
'filter_elements' => $historyHelper->getAssociatedElements($part),
|
||||
'mode' => 'element_history'
|
||||
], ['pageLength' => 10])
|
||||
->handleRequest($request);
|
||||
if ($this->isGranted('show_history', $part) ) {
|
||||
$table = $dataTable->createFromType(LogDataTable::class, [
|
||||
'filter_elements' => $historyHelper->getAssociatedElements($part),
|
||||
'mode' => 'element_history'
|
||||
], ['pageLength' => 10])
|
||||
->handleRequest($request);
|
||||
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
}
|
||||
} else {
|
||||
$table = null;
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
|
|
|
@ -42,11 +42,13 @@ class IconLinkColumn extends AbstractColumn
|
|||
'icon' => 'fas fa-fw fa-edit',
|
||||
'title' => null,
|
||||
'href' => null,
|
||||
'disabled' => false,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('title', ['null', 'string', 'callable']);
|
||||
$resolver->setAllowedTypes('icon', ['null', 'string', 'callable']);
|
||||
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
|
||||
$resolver->setAllowedTypes('disabled', ['bool', 'callable']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -56,10 +58,12 @@ class IconLinkColumn extends AbstractColumn
|
|||
$href = $this->getHref($value, $context);
|
||||
$icon = $this->getIcon($value, $context);
|
||||
$title = $this->getTitle($value, $context);
|
||||
$disabled = $this->getDisabled($value, $context);
|
||||
|
||||
if ($href !== null) {
|
||||
return sprintf(
|
||||
'<a class="btn btn-primary btn-sm" href="%s" title="%s"><i class="%s"></i></a>',
|
||||
'<a class="btn btn-primary btn-sm %s" href="%s" title="%s"><i class="%s"></i></a>',
|
||||
$disabled ? 'disabled' : '',
|
||||
$href,
|
||||
$title,
|
||||
$icon
|
||||
|
@ -69,6 +73,18 @@ class IconLinkColumn extends AbstractColumn
|
|||
return "";
|
||||
}
|
||||
|
||||
protected function getDisabled($value, $context): bool
|
||||
{
|
||||
$provider = $this->options['disabled'];
|
||||
if (is_bool($provider)) {
|
||||
return $provider;
|
||||
}
|
||||
if (is_callable($provider)) {
|
||||
return call_user_func($provider, $value, $context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getHref($value, $context): ?string
|
||||
{
|
||||
$provider = $this->options['href'];
|
||||
|
|
|
@ -27,15 +27,18 @@ use App\Entity\LogSystem\ElementCreatedLogEntry;
|
|||
use App\Entity\LogSystem\ElementDeletedLogEntry;
|
||||
use App\Entity\LogSystem\ElementEditedLogEntry;
|
||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class RevertLogColumn extends AbstractColumn
|
||||
{
|
||||
protected $translator;
|
||||
protected $security;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
public function __construct(TranslatorInterface $translator, Security $security)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,17 +68,21 @@ class RevertLogColumn extends AbstractColumn
|
|||
return '';
|
||||
}
|
||||
|
||||
$disabled = !$this->security->isGranted('revert_element', $context->getTargetClass());
|
||||
|
||||
$tmp = '<div class="btn-group btn-group-sm">';
|
||||
$tmp .= sprintf(
|
||||
'<button type="submit" class="btn btn-outline-secondary" name="undo" value="%d"><i class="fas fa-fw %s" title="%s"></i></button>',
|
||||
'<button type="submit" class="btn btn-outline-secondary" name="undo" value="%d" %s><i class="fas fa-fw %s" title="%s"></i></button>',
|
||||
$context->getID(),
|
||||
$disabled ? 'disabled' : '',
|
||||
$icon,
|
||||
$title
|
||||
);
|
||||
|
||||
$tmp .= sprintf(
|
||||
'<button type="submit" class="btn btn-outline-secondary" name="revert" value="%d"><i class="fas fa-fw fa-backward" title="%s"></i></button>',
|
||||
'<button type="submit" class="btn btn-outline-secondary" name="revert" value="%d" %s><i class="fas fa-fw fa-backward" title="%s"></i></button>',
|
||||
$context->getID(),
|
||||
$disabled ? 'disabled' : '',
|
||||
$this->translator->trans('log.undo.revert')
|
||||
);
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ use Omines\DataTablesBundle\DataTableTypeInterface;
|
|||
use Psr\Log\LogLevel;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Symfony\Flex\Options;
|
||||
|
||||
|
@ -76,15 +77,17 @@ class LogDataTable implements DataTableTypeInterface
|
|||
protected $urlGenerator;
|
||||
protected $entityURLGenerator;
|
||||
protected $logRepo;
|
||||
protected $security;
|
||||
|
||||
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
|
||||
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager)
|
||||
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager, Security $security)
|
||||
{
|
||||
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||
$this->translator = $translator;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->entityURLGenerator = $entityURLGenerator;
|
||||
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $optionsResolver)
|
||||
|
@ -235,7 +238,13 @@ class LogDataTable implements DataTableTypeInterface
|
|||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'disabled' => function ($value, AbstractLogEntry $context) {
|
||||
return
|
||||
!$this->security->isGranted('@tools.timetravel')
|
||||
|| !$this->security->isGranted('show_history', $context->getTargetClass());
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
$dataTable->add('actionRevert', RevertLogColumn::class, [
|
||||
|
|
|
@ -481,7 +481,7 @@ class PermissionsEmbed
|
|||
*/
|
||||
final protected static function readBitPair($data, int $n): int
|
||||
{
|
||||
Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
||||
//Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
||||
if (0 !== $n % 2) {
|
||||
throw new InvalidArgumentException('$n must be dividable by 2, because we address bit pairs here!');
|
||||
}
|
||||
|
@ -501,7 +501,7 @@ class PermissionsEmbed
|
|||
*/
|
||||
final protected static function writeBitPair(int $data, int $n, int $new): int
|
||||
{
|
||||
Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
||||
//Assert::lessThanEq($n, 31, '$n must be smaller than 32, because only a 32bit int is used! Got %s.');
|
||||
Assert::lessThanEq($new, 3, '$new must be smaller than 3, because a bit pair is written! Got %s.');
|
||||
Assert::greaterThanEq($new, 0, '$new must not be negative, because a bit pair is written! Got %s.');
|
||||
|
||||
|
|
|
@ -58,11 +58,7 @@ class AttachmentVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if ($subject instanceof Attachment) {
|
||||
return $this->resolver->inherit($user, 'parts_attachments', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->resolver->inherit($user, 'parts_attachments', $attribute) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,10 +71,11 @@ class AttachmentVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if ($subject instanceof Attachment) {
|
||||
if (is_a($subject, Attachment::class, true)) {
|
||||
return in_array($attribute, $this->resolver->listOperationsForPermission('parts_attachments'), false);
|
||||
}
|
||||
|
||||
//Allow class name as subject
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,11 +57,7 @@ class GroupVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if ($subject instanceof Group) {
|
||||
return $this->resolver->inherit($user, 'groups', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->resolver->inherit($user, 'groups', $attribute) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,7 +70,7 @@ class GroupVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if ($subject instanceof Group) {
|
||||
if (is_a($subject, Group::class, true)) {
|
||||
return $this->resolver->isValidOperation('groups', $attribute);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,25 +51,21 @@ class LogEntryVoter extends ExtendedVoter
|
|||
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if ($subject instanceof AbstractLogEntry) {
|
||||
if ('delete' === $attribute) {
|
||||
return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false;
|
||||
}
|
||||
|
||||
if ('read' === $attribute) {
|
||||
//Allow read of the users own log entries
|
||||
if (
|
||||
$subject->getUser() === $user
|
||||
&& $this->resolver->inherit($user, 'self', 'show_logs')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->resolver->inherit($user, 'system', 'show_logs') ?? false;
|
||||
}
|
||||
if ('delete' === $attribute) {
|
||||
return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if ('read' === $attribute) {
|
||||
//Allow read of the users own log entries
|
||||
if (
|
||||
$subject->getUser() === $user
|
||||
&& $this->resolver->inherit($user, 'self', 'show_logs')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->resolver->inherit($user, 'system', 'show_logs') ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function supports($attribute, $subject)
|
||||
|
|
57
src/Security/Voter/OrderdetailVoter.php
Normal file
57
src/Security/Voter/OrderdetailVoter.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\PriceInformations\Orderdetail;
|
||||
use App\Entity\UserSystem\User;
|
||||
|
||||
class OrderdetailVoter extends ExtendedVoter
|
||||
{
|
||||
/** @var string[] When this permsission are encountered, they are checked on part */
|
||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return $this->resolver->inherit($user, 'parts_orderdetails', $attribute) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if (is_a($subject, Orderdetail::class, true)) {
|
||||
return in_array($attribute, array_merge(
|
||||
self::PART_PERMS,
|
||||
$this->resolver->listOperationsForPermission('parts_orderdetails')
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
56
src/Security/Voter/PartLotVoter.php
Normal file
56
src/Security/Voter/PartLotVoter.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\UserSystem\User;
|
||||
|
||||
class PartLotVoter extends ExtendedVoter
|
||||
{
|
||||
/** @var string[] When this permsission are encountered, they are checked on part */
|
||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return $this->resolver->inherit($user, 'parts_lots', $attribute) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if (is_a($subject, PartLot::class, true)) {
|
||||
return in_array($attribute, array_merge(
|
||||
self::PART_PERMS,
|
||||
$this->resolver->listOperationsForPermission('parts_lots')
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,11 +57,7 @@ class PartVoter extends ExtendedVoter
|
|||
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
// replace with your own logic
|
||||
// https://symfony.com/doc/current/security/voters.html
|
||||
//return ($subject instanceof Part || in_array($subject, ['PERM_parts', 'PERM_parts_name']));
|
||||
|
||||
if ($subject instanceof Part) {
|
||||
if (is_a($subject, Part::class, true)) {
|
||||
//Check if a sub permission should be checked -> $attribute has format name.edit
|
||||
if (false !== strpos($attribute, '.')) {
|
||||
[$perm, $op] = explode('.', $attribute);
|
||||
|
@ -72,24 +68,21 @@ class PartVoter extends ExtendedVoter
|
|||
return $this->resolver->isValidOperation('parts', $attribute);
|
||||
}
|
||||
|
||||
//Allow class name as subject
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if ($subject instanceof Part) {
|
||||
//Check for sub permissions
|
||||
if (false !== strpos($attribute, '.')) {
|
||||
[$perm, $op] = explode('.', $attribute);
|
||||
//Check for sub permissions
|
||||
if (false !== strpos($attribute, '.')) {
|
||||
[$perm, $op] = explode('.', $attribute);
|
||||
|
||||
return $this->resolver->inherit($user, 'parts_'.$perm, $op) ?? false;
|
||||
}
|
||||
|
||||
//Null concealing operator means, that no
|
||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||
return $this->resolver->inherit($user, 'parts_'.$perm, $op) ?? false;
|
||||
}
|
||||
|
||||
//Deny access by default.
|
||||
return false;
|
||||
//Null concealing operator means, that no
|
||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
57
src/Security/Voter/PricedetailVoter.php
Normal file
57
src/Security/Voter/PricedetailVoter.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\PriceInformations\Pricedetail;
|
||||
use App\Entity\UserSystem\User;
|
||||
|
||||
class PricedetailVoter extends ExtendedVoter
|
||||
{
|
||||
/** @var string[] When this permsission are encountered, they are checked on part */
|
||||
protected const PART_PERMS = ['show_history', 'revert_element'];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||
{
|
||||
if (in_array($attribute, self::PART_PERMS, true)) {
|
||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return $this->resolver->inherit($user, 'parts_prices', $attribute) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if (is_a($subject, Pricedetail::class, true)) {
|
||||
return in_array($attribute, array_merge(
|
||||
self::PART_PERMS,
|
||||
$this->resolver->listOperationsForPermission('parts_prices')
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,11 +43,13 @@ declare(strict_types=1);
|
|||
namespace App\Security\Voter;
|
||||
|
||||
use App\Entity\Attachments\AttachmentType;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Entity\Devices\Device;
|
||||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Footprint;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Entity\Parts\Supplier;
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
|
@ -67,24 +69,29 @@ class StructureVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if (is_object($subject)) {
|
||||
if (is_object($subject) || is_string($subject)) {
|
||||
$permission_name = $this->instanceToPermissionName($subject);
|
||||
//If permission name is null, then the subject is not supported
|
||||
return (null !== $permission_name) && $this->resolver->isValidOperation($permission_name, $attribute);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a instance type to the permission name.
|
||||
*
|
||||
* @param mixed $subject The subject for which the permission name should be generated
|
||||
* @param object|string $subject The subject for which the permission name should be generated
|
||||
*
|
||||
* @return string|null the name of the permission for the subject's type or null, if the subject is not supported
|
||||
*/
|
||||
protected function instanceToPermissionName($subject): ?string
|
||||
{
|
||||
$class_name = get_class($subject);
|
||||
if (!is_string($subject)) {
|
||||
$class_name = get_class($subject);
|
||||
} else {
|
||||
$class_name = $subject;
|
||||
}
|
||||
switch ($class_name) {
|
||||
case AttachmentType::class:
|
||||
return 'attachment_types';
|
||||
|
|
|
@ -57,11 +57,11 @@ class UserVoter extends ExtendedVoter
|
|||
*/
|
||||
protected function supports($attribute, $subject)
|
||||
{
|
||||
if ($subject instanceof User) {
|
||||
if (is_a($subject, User::class, true)) {
|
||||
return in_array($attribute, array_merge(
|
||||
$this->resolver->listOperationsForPermission('users'),
|
||||
$this->resolver->listOperationsForPermission('self')),
|
||||
false
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -89,10 +89,11 @@ class UserVoter extends ExtendedVoter
|
|||
return $tmp;
|
||||
}
|
||||
}
|
||||
//Else just check users permission:
|
||||
if ($this->resolver->isValidOperation('users', $attribute)) {
|
||||
return $this->resolver->inherit($user, 'users', $attribute) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
//Else just check users permission:
|
||||
if ($this->resolver->isValidOperation('users', $attribute)) {
|
||||
return $this->resolver->inherit($user, 'users', $attribute) ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -59,8 +59,8 @@
|
|||
<ul class="nav nav-tabs mt-2">
|
||||
<li class="nav-item"><a class="link-anchor active nav-link" data-toggle="tab" href="#home">{% trans %}standard.label{% endtrans %}</a></li>
|
||||
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#info">{% trans %}infos.label{% endtrans %}</a></li>
|
||||
{% if datatable is defined and datatable is not null %}
|
||||
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#history">{% trans %}history.label{% endtrans %}</a></li>
|
||||
{% if datatable is defined %}
|
||||
<li class="nav-item {% if datatable is null %}not-allowed{% endif %}"><a data-toggle="tab" class="link-anchor nav-link {% if datatable is null %}disabled{% endif %}" href="#history">{% trans %}history.label{% endtrans %}</a></li>
|
||||
{% endif %}
|
||||
{% if entity.id %}
|
||||
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#export">{% trans %}export.label{% endtrans %}</a> </li>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<div class="mt-2">
|
||||
{% include "LogSystem/_log_table.html.twig" %}
|
||||
{% if datatable is not null %}
|
||||
{% include "LogSystem/_log_table.html.twig" %}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -72,8 +72,8 @@
|
|||
<span class="badge badge-secondary">{{ part.orderdetails | length }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="history-tab" data-toggle="tab" href="#history" role="tab">
|
||||
<li class="nav-item {% if datatable is null %}not-allowed{% endif %}">
|
||||
<a class="nav-link {% if datatable is null %}disabled{% endif %}" id="history-tab" data-toggle="tab" href="#history" role="tab">
|
||||
<i class="fas fa-history"></i>
|
||||
{% trans %}vendor.partinfo.history{% endtrans %}
|
||||
</a>
|
||||
|
|
|
@ -40,10 +40,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% if datatable is not null %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><i class="fas fa-fw fa-history"></i> {% trans %}homepage.last_activity{% endtrans %}</div>
|
||||
<div class="card-body">
|
||||
{% include "LogSystem/_log_table.html.twig" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue