mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +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
|
||||||
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:
|
show_history:
|
||||||
label: "perm.part.show_history"
|
label: "perm.part.show_history"
|
||||||
bit: 30
|
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.
|
parts_name: &PART_ATTRIBUTE # We define a template here, that we can use for all part attributes.
|
||||||
label: "perm.part.name"
|
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"
|
label: "perm.part.lots"
|
||||||
|
|
||||||
parts_attachments:
|
parts_attachments:
|
||||||
<<: *PART_MULTI_ATTRIBUTE
|
group: "structures"
|
||||||
label: "perm.part.attachments"
|
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:
|
parts_order:
|
||||||
<<: *PART_ATTRIBUTE
|
<<: *PART_ATTRIBUTE
|
||||||
|
@ -189,6 +216,13 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
show_users:
|
show_users:
|
||||||
label: "perm.show_users"
|
label: "perm.show_users"
|
||||||
bit: 12
|
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:
|
footprints:
|
||||||
<<: *PART_CONTAINING
|
<<: *PART_CONTAINING
|
||||||
|
@ -243,6 +277,12 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
|
||||||
statistics:
|
statistics:
|
||||||
label: "perm.tools.statistics"
|
label: "perm.tools.statistics"
|
||||||
bit: 10
|
bit: 10
|
||||||
|
lastActivity:
|
||||||
|
label: "perm.tools.lastActivity"
|
||||||
|
bit: 12
|
||||||
|
timetravel:
|
||||||
|
label: "perm.tools.timeTravel"
|
||||||
|
bit: 14
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
label: "perm.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"
|
label: "perm.edit_permissions"
|
||||||
alsoSet: ['read', 'edit']
|
alsoSet: ['read', 'edit']
|
||||||
bit: 10
|
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:
|
users:
|
||||||
label: "perm.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:
|
change_user_settings:
|
||||||
label: "perm.users.change_user_settings"
|
label: "perm.users.change_user_settings"
|
||||||
bit: 16
|
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:
|
database:
|
||||||
label: "perm.database"
|
label: "perm.database"
|
||||||
|
|
|
@ -119,6 +119,8 @@ abstract class BaseAdminController extends AbstractController
|
||||||
|
|
||||||
$timeTravel_timestamp = null;
|
$timeTravel_timestamp = null;
|
||||||
if ($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 the timestamp only contains numbers interpret it as unix timestamp
|
||||||
if (ctype_digit($timestamp)) {
|
if (ctype_digit($timestamp)) {
|
||||||
$timeTravel_timestamp = new \DateTime();
|
$timeTravel_timestamp = new \DateTime();
|
||||||
|
@ -129,15 +131,23 @@ abstract class BaseAdminController extends AbstractController
|
||||||
$this->timeTravel->revertEntityToTimestamp($entity, $timeTravel_timestamp);
|
$this->timeTravel->revertEntityToTimestamp($entity, $timeTravel_timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table = $this->dataTableFactory->createFromType(LogDataTable::class, [
|
if ($this->isGranted('show_history', $entity) ) {
|
||||||
|
$table = $this->dataTableFactory->createFromType(
|
||||||
|
LogDataTable::class,
|
||||||
|
[
|
||||||
'filter_elements' => $this->historyHelper->getAssociatedElements($entity),
|
'filter_elements' => $this->historyHelper->getAssociatedElements($entity),
|
||||||
'mode' => 'element_history'
|
'mode' => 'element_history'
|
||||||
], ['pageLength' => 10])
|
],
|
||||||
|
['pageLength' => 10]
|
||||||
|
)
|
||||||
->handleRequest($request);
|
->handleRequest($request);
|
||||||
|
|
||||||
if ($table->isCallback()) {
|
if ($table->isCallback()) {
|
||||||
return $table->getResponse();
|
return $table->getResponse();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$table = null;
|
||||||
|
}
|
||||||
|
|
||||||
$form = $this->createForm($this->form_class, $entity, [
|
$form = $this->createForm($this->form_class, $entity, [
|
||||||
'attachment_class' => $this->attachment_class,
|
'attachment_class' => $this->attachment_class,
|
||||||
|
|
|
@ -86,14 +86,22 @@ class HomepageController extends AbstractController
|
||||||
*/
|
*/
|
||||||
public function homepage(Request $request, GitVersionInfo $versionInfo): Response
|
public function homepage(Request $request, GitVersionInfo $versionInfo): Response
|
||||||
{
|
{
|
||||||
$table = $this->dataTable->createFromType(LogDataTable::class, [
|
if ($this->isGranted("@tools.lastActivity")) {
|
||||||
|
$table = $this->dataTable->createFromType(
|
||||||
|
LogDataTable::class,
|
||||||
|
[
|
||||||
'mode' => 'last_activity'
|
'mode' => 'last_activity'
|
||||||
], ['pageLength' => 10])
|
],
|
||||||
|
['pageLength' => 10]
|
||||||
|
)
|
||||||
->handleRequest($request);
|
->handleRequest($request);
|
||||||
|
|
||||||
if ($table->isCallback()) {
|
if ($table->isCallback()) {
|
||||||
return $table->getResponse();
|
return $table->getResponse();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$table = null;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->render('homepage.html.twig', [
|
return $this->render('homepage.html.twig', [
|
||||||
'banner' => $this->getBanner(),
|
'banner' => $this->getBanner(),
|
||||||
|
|
|
@ -121,6 +121,8 @@ class LogController extends AbstractController
|
||||||
throw new \InvalidArgumentException('No log entry with the given ID is existing!');
|
throw new \InvalidArgumentException('No log entry with the given ID is existing!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->denyAccessUnlessGranted('revert_element', $log_element->getTargetClass());
|
||||||
|
|
||||||
$eventUndoHelper->setMode($mode);
|
$eventUndoHelper->setMode($mode);
|
||||||
$eventUndoHelper->setUndoneEvent($log_element);
|
$eventUndoHelper->setUndoneEvent($log_element);
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,8 @@ class PartController extends AbstractController
|
||||||
|
|
||||||
$timeTravel_timestamp = null;
|
$timeTravel_timestamp = null;
|
||||||
if ($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 the timestamp only contains numbers interpret it as unix timestamp
|
||||||
if (ctype_digit($timestamp)) {
|
if (ctype_digit($timestamp)) {
|
||||||
$timeTravel_timestamp = new \DateTime();
|
$timeTravel_timestamp = new \DateTime();
|
||||||
|
@ -108,6 +110,7 @@ class PartController extends AbstractController
|
||||||
$timeTravel->revertEntityToTimestamp($part, $timeTravel_timestamp);
|
$timeTravel->revertEntityToTimestamp($part, $timeTravel_timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isGranted('show_history', $part) ) {
|
||||||
$table = $dataTable->createFromType(LogDataTable::class, [
|
$table = $dataTable->createFromType(LogDataTable::class, [
|
||||||
'filter_elements' => $historyHelper->getAssociatedElements($part),
|
'filter_elements' => $historyHelper->getAssociatedElements($part),
|
||||||
'mode' => 'element_history'
|
'mode' => 'element_history'
|
||||||
|
@ -117,6 +120,9 @@ class PartController extends AbstractController
|
||||||
if ($table->isCallback()) {
|
if ($table->isCallback()) {
|
||||||
return $table->getResponse();
|
return $table->getResponse();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$table = null;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'Parts/info/show_part_info.html.twig',
|
'Parts/info/show_part_info.html.twig',
|
||||||
|
|
|
@ -42,11 +42,13 @@ class IconLinkColumn extends AbstractColumn
|
||||||
'icon' => 'fas fa-fw fa-edit',
|
'icon' => 'fas fa-fw fa-edit',
|
||||||
'title' => null,
|
'title' => null,
|
||||||
'href' => null,
|
'href' => null,
|
||||||
|
'disabled' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$resolver->setAllowedTypes('title', ['null', 'string', 'callable']);
|
$resolver->setAllowedTypes('title', ['null', 'string', 'callable']);
|
||||||
$resolver->setAllowedTypes('icon', ['null', 'string', 'callable']);
|
$resolver->setAllowedTypes('icon', ['null', 'string', 'callable']);
|
||||||
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
|
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
|
||||||
|
$resolver->setAllowedTypes('disabled', ['bool', 'callable']);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -56,10 +58,12 @@ class IconLinkColumn extends AbstractColumn
|
||||||
$href = $this->getHref($value, $context);
|
$href = $this->getHref($value, $context);
|
||||||
$icon = $this->getIcon($value, $context);
|
$icon = $this->getIcon($value, $context);
|
||||||
$title = $this->getTitle($value, $context);
|
$title = $this->getTitle($value, $context);
|
||||||
|
$disabled = $this->getDisabled($value, $context);
|
||||||
|
|
||||||
if ($href !== null) {
|
if ($href !== null) {
|
||||||
return sprintf(
|
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,
|
$href,
|
||||||
$title,
|
$title,
|
||||||
$icon
|
$icon
|
||||||
|
@ -69,6 +73,18 @@ class IconLinkColumn extends AbstractColumn
|
||||||
return "";
|
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
|
protected function getHref($value, $context): ?string
|
||||||
{
|
{
|
||||||
$provider = $this->options['href'];
|
$provider = $this->options['href'];
|
||||||
|
|
|
@ -27,15 +27,18 @@ use App\Entity\LogSystem\ElementCreatedLogEntry;
|
||||||
use App\Entity\LogSystem\ElementDeletedLogEntry;
|
use App\Entity\LogSystem\ElementDeletedLogEntry;
|
||||||
use App\Entity\LogSystem\ElementEditedLogEntry;
|
use App\Entity\LogSystem\ElementEditedLogEntry;
|
||||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
use Omines\DataTablesBundle\Column\AbstractColumn;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class RevertLogColumn extends AbstractColumn
|
class RevertLogColumn extends AbstractColumn
|
||||||
{
|
{
|
||||||
protected $translator;
|
protected $translator;
|
||||||
|
protected $security;
|
||||||
|
|
||||||
public function __construct(TranslatorInterface $translator)
|
public function __construct(TranslatorInterface $translator, Security $security)
|
||||||
{
|
{
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
|
$this->security = $security;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,17 +68,21 @@ class RevertLogColumn extends AbstractColumn
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$disabled = !$this->security->isGranted('revert_element', $context->getTargetClass());
|
||||||
|
|
||||||
$tmp = '<div class="btn-group btn-group-sm">';
|
$tmp = '<div class="btn-group btn-group-sm">';
|
||||||
$tmp .= sprintf(
|
$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(),
|
$context->getID(),
|
||||||
|
$disabled ? 'disabled' : '',
|
||||||
$icon,
|
$icon,
|
||||||
$title
|
$title
|
||||||
);
|
);
|
||||||
|
|
||||||
$tmp .= sprintf(
|
$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(),
|
$context->getID(),
|
||||||
|
$disabled ? 'disabled' : '',
|
||||||
$this->translator->trans('log.undo.revert')
|
$this->translator->trans('log.undo.revert')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ use Omines\DataTablesBundle\DataTableTypeInterface;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Symfony\Flex\Options;
|
use Symfony\Flex\Options;
|
||||||
|
|
||||||
|
@ -76,15 +77,17 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
protected $urlGenerator;
|
protected $urlGenerator;
|
||||||
protected $entityURLGenerator;
|
protected $entityURLGenerator;
|
||||||
protected $logRepo;
|
protected $logRepo;
|
||||||
|
protected $security;
|
||||||
|
|
||||||
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
|
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->elementTypeNameGenerator = $elementTypeNameGenerator;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->entityURLGenerator = $entityURLGenerator;
|
$this->entityURLGenerator = $entityURLGenerator;
|
||||||
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
|
||||||
|
$this->security = $security;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $optionsResolver)
|
public function configureOptions(OptionsResolver $optionsResolver)
|
||||||
|
@ -235,7 +238,13 @@ class LogDataTable implements DataTableTypeInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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, [
|
$dataTable->add('actionRevert', RevertLogColumn::class, [
|
||||||
|
|
|
@ -481,7 +481,7 @@ class PermissionsEmbed
|
||||||
*/
|
*/
|
||||||
final protected static function readBitPair($data, int $n): int
|
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) {
|
if (0 !== $n % 2) {
|
||||||
throw new InvalidArgumentException('$n must be dividable by 2, because we address bit pairs here!');
|
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
|
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::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.');
|
Assert::greaterThanEq($new, 0, '$new must not be negative, because a bit pair is written! Got %s.');
|
||||||
|
|
||||||
|
|
|
@ -58,13 +58,9 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if ($subject instanceof Attachment) {
|
|
||||||
return $this->resolver->inherit($user, 'parts_attachments', $attribute) ?? false;
|
return $this->resolver->inherit($user, 'parts_attachments', $attribute) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the attribute and subject are supported by this voter.
|
* Determines if the attribute and subject are supported by this voter.
|
||||||
*
|
*
|
||||||
|
@ -75,10 +71,11 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function supports($attribute, $subject)
|
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);
|
return in_array($attribute, $this->resolver->listOperationsForPermission('parts_attachments'), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Allow class name as subject
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,13 +57,9 @@ class GroupVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if ($subject instanceof Group) {
|
|
||||||
return $this->resolver->inherit($user, 'groups', $attribute) ?? false;
|
return $this->resolver->inherit($user, 'groups', $attribute) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the attribute and subject are supported by this voter.
|
* Determines if the attribute and subject are supported by this voter.
|
||||||
*
|
*
|
||||||
|
@ -74,7 +70,7 @@ class GroupVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function supports($attribute, $subject)
|
protected function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
if ($subject instanceof Group) {
|
if (is_a($subject, Group::class, true)) {
|
||||||
return $this->resolver->isValidOperation('groups', $attribute);
|
return $this->resolver->isValidOperation('groups', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,6 @@ class LogEntryVoter extends ExtendedVoter
|
||||||
|
|
||||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if ($subject instanceof AbstractLogEntry) {
|
|
||||||
if ('delete' === $attribute) {
|
if ('delete' === $attribute) {
|
||||||
return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false;
|
return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false;
|
||||||
}
|
}
|
||||||
|
@ -69,9 +68,6 @@ class LogEntryVoter extends ExtendedVoter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function supports($attribute, $subject)
|
protected function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
if ($subject instanceof AbstractLogEntry) {
|
if ($subject instanceof AbstractLogEntry) {
|
||||||
|
|
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)
|
protected function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
// replace with your own logic
|
if (is_a($subject, Part::class, true)) {
|
||||||
// https://symfony.com/doc/current/security/voters.html
|
|
||||||
//return ($subject instanceof Part || in_array($subject, ['PERM_parts', 'PERM_parts_name']));
|
|
||||||
|
|
||||||
if ($subject instanceof Part) {
|
|
||||||
//Check if a sub permission should be checked -> $attribute has format name.edit
|
//Check if a sub permission should be checked -> $attribute has format name.edit
|
||||||
if (false !== strpos($attribute, '.')) {
|
if (false !== strpos($attribute, '.')) {
|
||||||
[$perm, $op] = explode('.', $attribute);
|
[$perm, $op] = explode('.', $attribute);
|
||||||
|
@ -72,12 +68,12 @@ class PartVoter extends ExtendedVoter
|
||||||
return $this->resolver->isValidOperation('parts', $attribute);
|
return $this->resolver->isValidOperation('parts', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Allow class name as subject
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnUser($attribute, $subject, User $user): bool
|
protected function voteOnUser($attribute, $subject, User $user): bool
|
||||||
{
|
{
|
||||||
if ($subject instanceof Part) {
|
|
||||||
//Check for sub permissions
|
//Check for sub permissions
|
||||||
if (false !== strpos($attribute, '.')) {
|
if (false !== strpos($attribute, '.')) {
|
||||||
[$perm, $op] = explode('.', $attribute);
|
[$perm, $op] = explode('.', $attribute);
|
||||||
|
@ -87,9 +83,6 @@ class PartVoter extends ExtendedVoter
|
||||||
|
|
||||||
//Null concealing operator means, that no
|
//Null concealing operator means, that no
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
||||||
}
|
|
||||||
|
|
||||||
//Deny access by default.
|
|
||||||
return 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;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\Attachments\AttachmentType;
|
use App\Entity\Attachments\AttachmentType;
|
||||||
|
use App\Entity\Base\AbstractStructuralDBElement;
|
||||||
use App\Entity\Devices\Device;
|
use App\Entity\Devices\Device;
|
||||||
use App\Entity\Parts\Category;
|
use App\Entity\Parts\Category;
|
||||||
use App\Entity\Parts\Footprint;
|
use App\Entity\Parts\Footprint;
|
||||||
use App\Entity\Parts\Manufacturer;
|
use App\Entity\Parts\Manufacturer;
|
||||||
use App\Entity\Parts\MeasurementUnit;
|
use App\Entity\Parts\MeasurementUnit;
|
||||||
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\Parts\Storelocation;
|
use App\Entity\Parts\Storelocation;
|
||||||
use App\Entity\Parts\Supplier;
|
use App\Entity\Parts\Supplier;
|
||||||
use App\Entity\PriceInformations\Currency;
|
use App\Entity\PriceInformations\Currency;
|
||||||
|
@ -67,24 +69,29 @@ class StructureVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function supports($attribute, $subject)
|
protected function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
if (is_object($subject)) {
|
if (is_object($subject) || is_string($subject)) {
|
||||||
$permission_name = $this->instanceToPermissionName($subject);
|
$permission_name = $this->instanceToPermissionName($subject);
|
||||||
//If permission name is null, then the subject is not supported
|
//If permission name is null, then the subject is not supported
|
||||||
return (null !== $permission_name) && $this->resolver->isValidOperation($permission_name, $attribute);
|
return (null !== $permission_name) && $this->resolver->isValidOperation($permission_name, $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a instance type to the permission name.
|
* 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
|
* @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
|
protected function instanceToPermissionName($subject): ?string
|
||||||
{
|
{
|
||||||
|
if (!is_string($subject)) {
|
||||||
$class_name = get_class($subject);
|
$class_name = get_class($subject);
|
||||||
|
} else {
|
||||||
|
$class_name = $subject;
|
||||||
|
}
|
||||||
switch ($class_name) {
|
switch ($class_name) {
|
||||||
case AttachmentType::class:
|
case AttachmentType::class:
|
||||||
return 'attachment_types';
|
return 'attachment_types';
|
||||||
|
|
|
@ -57,7 +57,7 @@ class UserVoter extends ExtendedVoter
|
||||||
*/
|
*/
|
||||||
protected function supports($attribute, $subject)
|
protected function supports($attribute, $subject)
|
||||||
{
|
{
|
||||||
if ($subject instanceof User) {
|
if (is_a($subject, User::class, true)) {
|
||||||
return in_array($attribute, array_merge(
|
return in_array($attribute, array_merge(
|
||||||
$this->resolver->listOperationsForPermission('users'),
|
$this->resolver->listOperationsForPermission('users'),
|
||||||
$this->resolver->listOperationsForPermission('self')),
|
$this->resolver->listOperationsForPermission('self')),
|
||||||
|
@ -89,11 +89,12 @@ class UserVoter extends ExtendedVoter
|
||||||
return $tmp;
|
return $tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Else just check users permission:
|
//Else just check users permission:
|
||||||
if ($this->resolver->isValidOperation('users', $attribute)) {
|
if ($this->resolver->isValidOperation('users', $attribute)) {
|
||||||
return $this->resolver->inherit($user, 'users', $attribute) ?? false;
|
return $this->resolver->inherit($user, 'users', $attribute) ?? false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,8 @@
|
||||||
<ul class="nav nav-tabs mt-2">
|
<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 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>
|
<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 %}
|
{% if datatable is defined %}
|
||||||
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#history">{% trans %}history.label{% endtrans %}</a></li>
|
<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 %}
|
{% endif %}
|
||||||
{% if entity.id %}
|
{% if entity.id %}
|
||||||
<li class="nav-item"><a data-toggle="tab" class="link-anchor nav-link" href="#export">{% trans %}export.label{% endtrans %}</a> </li>
|
<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">
|
<div class="mt-2">
|
||||||
|
{% if datatable is not null %}
|
||||||
{% include "LogSystem/_log_table.html.twig" %}
|
{% include "LogSystem/_log_table.html.twig" %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
|
@ -72,8 +72,8 @@
|
||||||
<span class="badge badge-secondary">{{ part.orderdetails | length }}</span>
|
<span class="badge badge-secondary">{{ part.orderdetails | length }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item {% if datatable is null %}not-allowed{% endif %}">
|
||||||
<a class="nav-link" id="history-tab" data-toggle="tab" href="#history" role="tab">
|
<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>
|
<i class="fas fa-history"></i>
|
||||||
{% trans %}vendor.partinfo.history{% endtrans %}
|
{% trans %}vendor.partinfo.history{% endtrans %}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -40,10 +40,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if datatable is not null %}
|
||||||
<div class="card mt-3">
|
<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-header"><i class="fas fa-fw fa-history"></i> {% trans %}homepage.last_activity{% endtrans %}</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% include "LogSystem/_log_table.html.twig" %}
|
{% include "LogSystem/_log_table.html.twig" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue