mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-03 22:09:35 +00:00
Compare commits
11 commits
0717239296
...
9b17efc12c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b17efc12c | ||
|
|
fe7910a2f2 | ||
|
|
eb4258053e | ||
|
|
117ff4484d | ||
|
|
ba7d139f8a | ||
|
|
d657b2ff04 | ||
|
|
0637c05053 | ||
|
|
88fbc46325 | ||
|
|
379155e839 | ||
|
|
c9a1febc56 | ||
|
|
7f099972e1 |
23 changed files with 224 additions and 57 deletions
25
composer.lock
generated
25
composer.lock
generated
|
|
@ -7436,16 +7436,16 @@
|
|||
},
|
||||
{
|
||||
"name": "part-db/exchanger",
|
||||
"version": "v3.0.0",
|
||||
"version": "v3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Part-DB/exchanger.git",
|
||||
"reference": "a549f2bd526042f66ad5caa044fd15c67ac5270f"
|
||||
"reference": "a43fe79a082e331ec2b24f3579e4fba153743757"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Part-DB/exchanger/zipball/a549f2bd526042f66ad5caa044fd15c67ac5270f",
|
||||
"reference": "a549f2bd526042f66ad5caa044fd15c67ac5270f",
|
||||
"url": "https://api.github.com/repos/Part-DB/exchanger/zipball/a43fe79a082e331ec2b24f3579e4fba153743757",
|
||||
"reference": "a43fe79a082e331ec2b24f3579e4fba153743757",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7507,9 +7507,9 @@
|
|||
"money"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/Part-DB/exchanger/tree/v3.0.0"
|
||||
"source": "https://github.com/Part-DB/exchanger/tree/v3.1.0"
|
||||
},
|
||||
"time": "2025-09-05T14:02:04+00:00"
|
||||
"time": "2025-09-05T19:48:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "part-db/label-fonts",
|
||||
|
|
@ -7620,19 +7620,20 @@
|
|||
},
|
||||
{
|
||||
"name": "part-db/swap-bundle",
|
||||
"version": "v6.0.0",
|
||||
"version": "v6.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Part-DB/symfony-swap.git",
|
||||
"reference": "6772eda2603a864b5f0a94224e0cfd79976c7389"
|
||||
"reference": "fd78ebfbd762b1d76b4d71f713f39add63dec62b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Part-DB/symfony-swap/zipball/6772eda2603a864b5f0a94224e0cfd79976c7389",
|
||||
"reference": "6772eda2603a864b5f0a94224e0cfd79976c7389",
|
||||
"url": "https://api.github.com/repos/Part-DB/symfony-swap/zipball/fd78ebfbd762b1d76b4d71f713f39add63dec62b",
|
||||
"reference": "fd78ebfbd762b1d76b4d71f713f39add63dec62b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"part-db/exchanger": "^3.1.0",
|
||||
"part-db/swap": "^5.0",
|
||||
"php": "^7.1.3|^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
|
|
@ -7687,9 +7688,9 @@
|
|||
"symfony"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/Part-DB/symfony-swap/tree/v6.0.0"
|
||||
"source": "https://github.com/Part-DB/symfony-swap/tree/v6.1.0"
|
||||
},
|
||||
"time": "2025-09-05T17:26:07+00:00"
|
||||
"time": "2025-09-05T19:52:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/discovery",
|
||||
|
|
|
|||
|
|
@ -11,3 +11,6 @@ florianv_swap:
|
|||
|
||||
fixer: # Fixer.io (needs an API key)
|
||||
access_key: "%env(string:settings:exchange_rate:fixerApiKey)%"
|
||||
|
||||
frankfurter: ~
|
||||
fawazahmed_currency_api: ~
|
||||
|
|
|
|||
|
|
@ -28,9 +28,14 @@ It is recommended to install Part-DB on a 64-bit system, as the 32-bit version o
|
|||
For the installation of Part-DB, we need some prerequisites. They can be installed by running the following command:
|
||||
|
||||
```bash
|
||||
sudo apt install git curl zip ca-certificates software-properties-common apt-transport-https lsb-release nano wget
|
||||
sudo apt update && apt upgrade
|
||||
sudo apt install git curl zip ca-certificates software-properties-common \
|
||||
apt-transport-https lsb-release nano wget sqlite3
|
||||
```
|
||||
|
||||
Please run `sqlite3 --version` to assert that the SQLite version is 3.35 or higher.
|
||||
Otherwise some database migrations will not succeed.
|
||||
|
||||
### Install PHP and apache2
|
||||
|
||||
Part-DB is written in [PHP](https://php.net) and therefore needs a PHP interpreter to run. Part-DB needs PHP 8.2 or
|
||||
|
|
|
|||
64
src/DataFixtures/CurrencyFixtures.php
Normal file
64
src/DataFixtures/CurrencyFixtures.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2025 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace App\DataFixtures;
|
||||
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use Brick\Math\BigDecimal;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
class CurrencyFixtures extends Fixture
|
||||
{
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$currency1 = new Currency();
|
||||
$currency1->setName('US-Dollar');
|
||||
$currency1->setIsoCode('USD');
|
||||
$manager->persist($currency1);
|
||||
|
||||
$currency2 = new Currency();
|
||||
$currency2->setName('Swiss Franc');
|
||||
$currency2->setIsoCode('CHF');
|
||||
$currency2->setExchangeRate(BigDecimal::of('0.91'));
|
||||
$manager->persist($currency2);
|
||||
|
||||
$currency3 = new Currency();
|
||||
$currency3->setName('Great British Pound');
|
||||
$currency3->setIsoCode('GBP');
|
||||
$currency3->setExchangeRate(BigDecimal::of('0.78'));
|
||||
$manager->persist($currency3);
|
||||
|
||||
$currency7 = new Currency();
|
||||
$currency7->setName('Test Currency with long name');
|
||||
$currency7->setIsoCode('CNY');
|
||||
$manager->persist($currency7);
|
||||
|
||||
$manager->flush();
|
||||
|
||||
|
||||
//Ensure that currency 7 gets ID 7
|
||||
$manager->getRepository(Currency::class)->changeID($currency7, 7);
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ use App\Entity\Attachments\UserAttachment;
|
|||
use RuntimeException;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
use function in_array;
|
||||
|
|
@ -56,7 +57,7 @@ final class AttachmentVoter extends Voter
|
|||
{
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
|
||||
//This voter only works for attachments
|
||||
|
|
@ -65,7 +66,8 @@ final class AttachmentVoter extends Voter
|
|||
}
|
||||
|
||||
if ($attribute === 'show_private') {
|
||||
return $this->helper->isGranted($token, 'attachments', 'show_private');
|
||||
$vote?->addReason('User is not allowed to view private attachments.');
|
||||
return $this->helper->isGranted($token, 'attachments', 'show_private', $vote);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -111,7 +113,8 @@ final class AttachmentVoter extends Voter
|
|||
throw new RuntimeException('Encountered unknown Parameter type: ' . $subject);
|
||||
}
|
||||
|
||||
return $this->helper->isGranted($token, $param, $this->mapOperation($attribute));
|
||||
$vote?->addReason('User is not allowed to '.$this->mapOperation($attribute).' attachments of type '.$param.'.');
|
||||
return $this->helper->isGranted($token, $param, $this->mapOperation($attribute), $vote);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace App\Security\Voter;
|
|||
use App\Entity\UserSystem\Group;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -43,9 +44,9 @@ final class GroupVoter extends Voter
|
|||
*
|
||||
* @param string $attribute
|
||||
*/
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
return $this->helper->isGranted($token, 'groups', $attribute);
|
||||
return $this->helper->isGranted($token, 'groups', $attribute, $vote);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace App\Security\Voter;
|
|||
use App\Entity\UserSystem\User;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
|
|
@ -47,9 +48,16 @@ final class ImpersonateUserVoter extends Voter
|
|||
&& $subject instanceof UserInterface;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
return $this->helper->isGranted($token, 'users', 'impersonate');
|
||||
$result = $this->helper->isGranted($token, 'users', 'impersonate');
|
||||
|
||||
if ($result === false) {
|
||||
$vote?->addReason('User is not allowed to impersonate other users.');
|
||||
$this->helper->addReason($vote, 'users', 'impersonate');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function supportsAttribute(string $attribute): bool
|
||||
|
|
@ -61,4 +69,4 @@ final class ImpersonateUserVoter extends Voter
|
|||
{
|
||||
return is_a($subjectType, User::class, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ namespace App\Security\Voter;
|
|||
use App\Entity\LabelSystem\LabelProfile;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -63,9 +64,9 @@ final class LabelProfileVoter extends Voter
|
|||
public function __construct(private readonly VoterHelper $helper)
|
||||
{}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute]);
|
||||
return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute], $vote);
|
||||
}
|
||||
|
||||
protected function supports($attribute, $subject): bool
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use App\Services\UserSystem\VoterHelper;
|
|||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use App\Entity\LogSystem\AbstractLogEntry;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -39,7 +40,7 @@ final class LogEntryVoter extends Voter
|
|||
{
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$user = $this->helper->resolveUser($token);
|
||||
|
||||
|
|
@ -48,19 +49,19 @@ final class LogEntryVoter extends Voter
|
|||
}
|
||||
|
||||
if ('delete' === $attribute) {
|
||||
return $this->helper->isGranted($token, 'system', 'delete_logs');
|
||||
return $this->helper->isGranted($token, 'system', 'delete_logs', $vote);
|
||||
}
|
||||
|
||||
if ('read' === $attribute) {
|
||||
//Allow read of the users own log entries
|
||||
if (
|
||||
$subject->getUser() === $user
|
||||
&& $this->helper->isGranted($token, 'self', 'show_logs')
|
||||
&& $this->helper->isGranted($token, 'self', 'show_logs', $vote)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->helper->isGranted($token, 'system', 'show_logs');
|
||||
return $this->helper->isGranted($token, 'system', 'show_logs', $vote);
|
||||
}
|
||||
|
||||
if ('show_details' === $attribute) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use Symfony\Bundle\SecurityBundle\Security;
|
|||
use App\Entity\Parts\Part;
|
||||
use App\Entity\PriceInformations\Orderdetail;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +60,7 @@ final class OrderdetailVoter extends Voter
|
|||
|
||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
if (! is_a($subject, Orderdetail::class, true)) {
|
||||
throw new \RuntimeException('This voter can only handle Orderdetail objects!');
|
||||
|
|
@ -75,7 +76,7 @@ final class OrderdetailVoter extends Voter
|
|||
|
||||
//If we have no part associated use the generic part permission
|
||||
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
||||
return $this->helper->isGranted($token, 'parts', $operation);
|
||||
return $this->helper->isGranted($token, 'parts', $operation, $vote);
|
||||
}
|
||||
|
||||
//Otherwise vote on the part
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use App\Entity\Parameters\StorageLocationParameter;
|
|||
use App\Entity\Parameters\SupplierParameter;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -53,7 +54,7 @@ final class ParameterVoter extends Voter
|
|||
{
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
||||
|
||||
|
|
@ -108,7 +109,7 @@ final class ParameterVoter extends Voter
|
|||
throw new RuntimeException('Encountered unknown Parameter type: ' . (is_object($subject) ? $subject::class : $subject));
|
||||
}
|
||||
|
||||
return $this->helper->isGranted($token, $param, $attribute);
|
||||
return $this->helper->isGranted($token, $param, $attribute, $vote);
|
||||
}
|
||||
|
||||
protected function supports(string $attribute, $subject): bool
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use App\Services\UserSystem\VoterHelper;
|
|||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use App\Entity\Parts\Part;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -61,7 +62,7 @@ final class PartAssociationVoter extends Voter
|
|||
|
||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
if (!is_string($subject) && !$subject instanceof PartAssociation) {
|
||||
throw new \RuntimeException('Invalid subject type!');
|
||||
|
|
@ -77,7 +78,7 @@ final class PartAssociationVoter extends Voter
|
|||
|
||||
//If we have no part associated use the generic part permission
|
||||
if (is_string($subject) || !$subject->getOwner() instanceof Part) {
|
||||
return $this->helper->isGranted($token, 'parts', $operation);
|
||||
return $this->helper->isGranted($token, 'parts', $operation, $vote);
|
||||
}
|
||||
|
||||
//Otherwise vote on the part
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use Symfony\Bundle\SecurityBundle\Security;
|
|||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -59,13 +60,13 @@ final class PartLotVoter extends Voter
|
|||
|
||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element', 'withdraw', 'add', 'move'];
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$user = $this->helper->resolveUser($token);
|
||||
|
||||
if (in_array($attribute, ['withdraw', 'add', 'move'], true))
|
||||
{
|
||||
$base_permission = $this->helper->isGranted($token, 'parts_stock', $attribute);
|
||||
$base_permission = $this->helper->isGranted($token, 'parts_stock', $attribute, $vote);
|
||||
|
||||
$lot_permission = true;
|
||||
//If the lot has an owner, we need to check if the user is the owner of the lot to be allowed to withdraw it.
|
||||
|
|
@ -73,6 +74,10 @@ final class PartLotVoter extends Voter
|
|||
$lot_permission = $subject->getOwner() === $user || $subject->getOwner()->getID() === $user->getID();
|
||||
}
|
||||
|
||||
if (!$lot_permission) {
|
||||
$vote->addReason('User is not the owner of the lot.');
|
||||
}
|
||||
|
||||
return $base_permission && $lot_permission;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +91,7 @@ final class PartLotVoter extends Voter
|
|||
|
||||
//If we have no part associated use the generic part permission
|
||||
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
||||
return $this->helper->isGranted($token, 'parts', $operation);
|
||||
return $this->helper->isGranted($token, 'parts', $operation, $vote);
|
||||
}
|
||||
|
||||
//Otherwise vote on the part
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace App\Security\Voter;
|
|||
use App\Entity\Parts\Part;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -52,10 +53,9 @@ final class PartVoter extends Voter
|
|||
return false;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
//Null concealing operator means, that no
|
||||
return $this->helper->isGranted($token, 'parts', $attribute);
|
||||
return $this->helper->isGranted($token, 'parts', $attribute, $vote);
|
||||
}
|
||||
|
||||
public function supportsAttribute(string $attribute): bool
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace App\Security\Voter;
|
|||
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -39,12 +40,17 @@ final class PermissionVoter extends Voter
|
|||
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$attribute = ltrim($attribute, '@');
|
||||
[$perm, $op] = explode('.', $attribute);
|
||||
|
||||
return $this->helper->isGranted($token, $perm, $op);
|
||||
$result = $this->helper->isGranted($token, $perm, $op);
|
||||
if ($result === false) {
|
||||
$this->helper->addReason($vote, $perm, $op);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function supportsAttribute(string $attribute): bool
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ use App\Entity\PriceInformations\Orderdetail;
|
|||
use App\Entity\Parts\Part;
|
||||
use App\Entity\PriceInformations\Pricedetail;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +61,7 @@ final class PricedetailVoter extends Voter
|
|||
|
||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$operation = match ($attribute) {
|
||||
'read' => 'read',
|
||||
|
|
@ -72,7 +73,7 @@ final class PricedetailVoter extends Voter
|
|||
|
||||
//If we have no part associated use the generic part permission
|
||||
if (is_string($subject) || !$subject->getOrderdetail() instanceof Orderdetail || !$subject->getOrderdetail()->getPart() instanceof Part) {
|
||||
return $this->helper->isGranted($token, 'parts', $operation);
|
||||
return $this->helper->isGranted($token, 'parts', $operation, $vote);
|
||||
}
|
||||
|
||||
//Otherwise vote on the part
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ use App\Entity\Parts\Supplier;
|
|||
use App\Entity\PriceInformations\Currency;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
use function is_object;
|
||||
|
|
@ -113,10 +114,10 @@ final class StructureVoter extends Voter
|
|||
*
|
||||
* @param string $attribute
|
||||
*/
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$permission_name = $this->instanceToPermissionName($subject);
|
||||
//Just resolve the permission
|
||||
return $this->helper->isGranted($token, $permission_name, $attribute);
|
||||
return $this->helper->isGranted($token, $permission_name, $attribute, $vote);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use App\Entity\UserSystem\User;
|
|||
use App\Services\UserSystem\PermissionManager;
|
||||
use App\Services\UserSystem\VoterHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
use function in_array;
|
||||
|
|
@ -79,7 +80,7 @@ final class UserVoter extends Voter
|
|||
*
|
||||
* @param string $attribute
|
||||
*/
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$user = $this->helper->resolveUser($token);
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ final class UserVoter extends Voter
|
|||
if (($subject instanceof User) && $subject->getID() === $user->getID() &&
|
||||
$this->helper->isValidOperation('self', $attribute)) {
|
||||
//Then we also need to check the self permission
|
||||
$tmp = $this->helper->isGranted($token, 'self', $attribute);
|
||||
$tmp = $this->helper->isGranted($token, 'self', $attribute, $vote);
|
||||
//But if the self value is not allowed then use just the user value:
|
||||
if ($tmp) {
|
||||
return $tmp;
|
||||
|
|
@ -106,7 +107,7 @@ final class UserVoter extends Voter
|
|||
|
||||
//Else just check user permission:
|
||||
if ($this->helper->isValidOperation('users', $attribute)) {
|
||||
return $this->helper->isGranted($token, 'users', $attribute);
|
||||
return $this->helper->isGranted($token, 'users', $attribute, $vote);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ use App\Repository\UserRepository;
|
|||
use App\Security\ApiTokenAuthenticatedToken;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @see \App\Tests\Services\UserSystem\VoterHelperTest
|
||||
|
|
@ -35,10 +38,14 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|||
final class VoterHelper
|
||||
{
|
||||
private readonly UserRepository $userRepository;
|
||||
private readonly array $permissionStructure;
|
||||
|
||||
public function __construct(private readonly PermissionManager $permissionManager, private readonly EntityManagerInterface $entityManager)
|
||||
public function __construct(private readonly PermissionManager $permissionManager,
|
||||
private readonly TranslatorInterface $translator,
|
||||
private readonly EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->userRepository = $this->entityManager->getRepository(User::class);
|
||||
$this->permissionStructure = $this->permissionManager->getPermissionStructure();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -47,11 +54,16 @@ final class VoterHelper
|
|||
* @param TokenInterface $token The token to check
|
||||
* @param string $permission The permission to check
|
||||
* @param string $operation The operation to check
|
||||
* @param Vote|null $vote The vote object to add reasons to (optional). If null, no reasons are added.
|
||||
* @return bool
|
||||
*/
|
||||
public function isGranted(TokenInterface $token, string $permission, string $operation): bool
|
||||
public function isGranted(TokenInterface $token, string $permission, string $operation, ?Vote $vote = null): bool
|
||||
{
|
||||
return $this->isGrantedTrinary($token, $permission, $operation) ?? false;
|
||||
$tmp = $this->isGrantedTrinary($token, $permission, $operation) ?? false;
|
||||
if ($tmp === false) {
|
||||
$this->addReason($vote, $permission, $operation);
|
||||
}
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,4 +136,17 @@ final class VoterHelper
|
|||
{
|
||||
return $this->permissionManager->isValidOperation($permission, $operation);
|
||||
}
|
||||
}
|
||||
|
||||
public function addReason(?Vote $voter, string $permission, $operation): void
|
||||
{
|
||||
if ($voter !== null) {
|
||||
$voter->addReason(sprintf("User does not have permission %s -> %s -> %s (%s.%s).",
|
||||
$this->translator->trans('perm.group.'.($this->permissionStructure['perms'][$permission]['group'] ?? 'unknown') ),
|
||||
$this->translator->trans($this->permissionStructure['perms'][$permission]['label'] ?? $permission),
|
||||
$this->translator->trans($this->permissionStructure['perms'][$permission]['operations'][$operation]['label'] ?? $operation),
|
||||
$permission,
|
||||
$operation
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
{% extends "bundles/TwigBundle/Exception/error.html.twig" %}
|
||||
|
||||
{% block status_comment %}
|
||||
Nice try! But you are not allowed to do this!
|
||||
Nice try! But you are not allowed to do this!<br>
|
||||
<code>{{ exception.message }}</code>
|
||||
<br> <small>If you think you should have access to this ressource, contact the adminstrator.</small>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="col-6">
|
||||
{% if provider.providerInfo.settings_class is defined %}
|
||||
<a href="{{ path('info_providers_provider_settings', {'provider': provider.providerKey}) }}" class="btn btn-primary btn-sm"
|
||||
<a href="{{ path('info_providers_provider_settings', {'provider': provider.providerKey}) }}" class="btn btn-primary btn-sm {% if not is_granted('@config.change_system_settings') %}disabled{% endif %}"
|
||||
title="{% trans %}info_providers.settings.title{% endtrans %}"
|
||||
><i class="fa-solid fa-cog"></i></a>
|
||||
{% endif %}
|
||||
|
|
|
|||
35
tests/Controller/AdminPages/CurrencyController.php
Normal file
35
tests/Controller/AdminPages/CurrencyController.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Controller\AdminPages;
|
||||
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
|
||||
#[Group('slow')]
|
||||
#[Group('DB')]
|
||||
class CurrencyController extends AbstractAdminController
|
||||
{
|
||||
protected static string $base_path = '/en/currency';
|
||||
protected static string $entity_class = Currency::class;
|
||||
}
|
||||
|
|
@ -7164,8 +7164,8 @@ Exampletown</target>
|
|||
Element 2
|
||||
Element 3
|
||||
|
||||
Element 1 -> Element 1.1
|
||||
Element 1 -> Element 1.2]]></target>
|
||||
Element 1 -> Element 1.1
|
||||
Element 1 -> Element 1.2]]></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="TWSqPFi" name="entity.mass_creation.btn">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue