refs #436. Improvements

pull/7/head
Manuel Aranda Rosales 2024-07-12 13:11:38 +02:00
parent f9bf9b6190
commit e4e9e3c07f
11 changed files with 138 additions and 49 deletions

View File

@ -17,6 +17,9 @@ api_platform:
mapping: mapping:
paths: ['%kernel.project_dir%/config/api_platform', '%kernel.project_dir%/src/Dto'] paths: ['%kernel.project_dir%/config/api_platform', '%kernel.project_dir%/src/Dto']
use_symfony_listeners: true use_symfony_listeners: true
collection:
pagination:
items_per_page_parameter_name: 'itemsPerPage'
defaults: defaults:
pagination_client_items_per_page: true pagination_client_items_per_page: true
denormalization_context: denormalization_context:

View File

@ -1,3 +1,6 @@
imports:
- { resource: 'services/api_platform.yaml' }
parameters: parameters:
services: services:

View File

@ -4,109 +4,93 @@ services:
arguments: arguments:
$properties: { 'id': ~, 'name': ~, 'serialNumber': ~ } $properties: { 'id': ~, 'name': ~, 'serialNumber': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.client.search: api_platform.filter.client.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact' } ] arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.hardware.order: api_platform.filter.hardware.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'name': ~ } $properties: { 'id': ~, 'name': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.hardware.search: api_platform.filter.hardware.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'partial' } ] arguments: [ { 'id': 'exact', 'name': 'partial' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.menu.order: api_platform.filter.menu.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'name': ~, 'title': ~ } $properties: { 'id': ~, 'name': ~, 'title': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.menu.search: api_platform.filter.menu.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'exact', 'title': 'exact' } ] arguments: [ { 'id': 'exact', 'name': 'exact', 'title': 'exact' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.organizational_unit.order: api_platform.filter.organizational_unit.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'name': ~, 'type': ~ } $properties: { 'id': ~, 'name': ~, 'type': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.organizational_unit.search: api_platform.filter.organizational_unit.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'exact', 'type': 'exact' } ] arguments: [ { id: 'exact', name: 'partial', type: 'exact', parent.id: 'exact'} ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.partition.order: api_platform.filter.partition.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'usage': ~ } $properties: { 'id': ~, 'usage': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.partition.search: api_platform.filter.partition.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'usage': 'exact', 'diskNumber': 'exact' } ] arguments: [ { 'id': 'exact', 'usage': 'exact', 'diskNumber': 'exact' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user.order: api_platform.filter.user.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'username': ~ } $properties: { 'id': ~, 'username': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user.search: api_platform.filter.user.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'username': 'partial' } ] arguments: [ { 'id': 'exact', 'username': 'partial' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user.boolean: api_platform.filter.user.boolean:
parent: 'api_platform.doctrine.orm.boolean_filter' parent: 'api_platform.doctrine.orm.boolean_filter'
arguments: [ { 'enabled': ~ } ] arguments: [ { 'enabled': ~ } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user_group.order: api_platform.filter.user_group.order:
parent: 'api_platform.doctrine.orm.order_filter' parent: 'api_platform.doctrine.orm.order_filter'
arguments: arguments:
$properties: { 'id': ~, 'name': ~ } $properties: { 'id': ~, 'name': ~ }
$orderParameterName: 'order' $orderParameterName: 'order'
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user_group.search: api_platform.filter.user_group.search:
parent: 'api_platform.doctrine.orm.search_filter' parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'partial' } ] arguments: [ { 'id': 'exact', 'name': 'partial' } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]
api_platform.filter.user_group.boolean: api_platform.filter.user_group.boolean:
parent: 'api_platform.doctrine.orm.boolean_filter' parent: 'api_platform.doctrine.orm.boolean_filter'
arguments: [ { 'enabled': ~ } ] arguments: [ { 'enabled': ~ } ]
tags: tags: [ 'api_platform.filter' ]
- [ 'api_platform.filter' ]

View File

@ -87,9 +87,10 @@ class MigrateHardwareAndHardwareProfileCommand extends Command
$migrationId = $hardware['hardwares.grupoid'] === 0 ? $hardware['hardwares.idcentro'] : $hardware['hardwares.grupoid']; $migrationId = $hardware['hardwares.grupoid'] === 0 ? $hardware['hardwares.idcentro'] : $hardware['hardwares.grupoid'];
$organizationalUnit = $organizationalUnitRepository->findOneBy(['migrationId' => $migrationId]); $organizationalUnit = $organizationalUnitRepository->findOneBy(['migrationId' => $migrationId]);
/*
if ($organizationalUnit){ if ($organizationalUnit){
$hardwareEntity->setOrganizationalUnit($organizationalUnit); $hardwareEntity->setOrganizationalUnit($organizationalUnit);
} }*/
$this->entityManager->persist($hardwareEntity); $this->entityManager->persist($hardwareEntity);
} }
@ -142,9 +143,9 @@ class MigrateHardwareAndHardwareProfileCommand extends Command
if ($hardwareProfileEntity && $hardwareEntity){ if ($hardwareProfileEntity && $hardwareEntity){
$hardwareProfileEntity->addHardwareCollection($hardwareEntity); $hardwareProfileEntity->addHardwareCollection($hardwareEntity);
}
$this->entityManager->persist($hardwareProfileEntity); $this->entityManager->persist($hardwareProfileEntity);
} }
}
$this->entityManager->flush(); $this->entityManager->flush();

View File

@ -36,13 +36,26 @@ readonly class UserAllowedOrganizationalUnitExtension implements QueryCollection
if (OrganizationalUnit::class !== $resourceClass || null === $user || in_array('ROLE_SUPER_ADMIN', $user->getRoles())) { if (OrganizationalUnit::class !== $resourceClass || null === $user || in_array('ROLE_SUPER_ADMIN', $user->getRoles())) {
return; return;
} }
$organizationalUnitIds = []; $organizationalUnitIds = [];
foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) { foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) {
$organizationalUnitIds[] = $allowedOrganizationalUnit->getId(); $this->addOrganizationalUnitAndChildrenIds($allowedOrganizationalUnit, $organizationalUnitIds);
} }
$rootAlias = $queryBuilder->getRootAliases()[0]; $rootAlias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere(sprintf('%s.id in (:ou)', $rootAlias)); $queryBuilder->andWhere(sprintf('%s.id in (:ou)', $rootAlias));
$queryBuilder->setParameter('ou', $organizationalUnitIds); $queryBuilder->setParameter('ou', $organizationalUnitIds);
} }
private function addOrganizationalUnitAndChildrenIds(OrganizationalUnit $organizationalUnit, array &$organizationalUnitIds): void
{
if (!in_array($organizationalUnit->getId(), $organizationalUnitIds)) {
$organizationalUnitIds[] = $organizationalUnit->getId();
}
foreach ($organizationalUnit->getOrganizationalUnits() as $child) {
$this->addOrganizationalUnitAndChildrenIds($child, $organizationalUnitIds);
}
}
} }

View File

@ -3,6 +3,7 @@
namespace App\Dto\Input; namespace App\Dto\Input;
use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiProperty;
use App\Dto\Output\OrganizationalUnitOutput;
use App\Dto\Output\UserGroupOutput; use App\Dto\Output\UserGroupOutput;
use App\Entity\OrganizationalUnit; use App\Entity\OrganizationalUnit;
use App\Entity\User; use App\Entity\User;
@ -17,7 +18,7 @@ final class UserInput
public ?string $username = null; public ?string $username = null;
/** /**
* @var OrganizationalUnit[] * @var OrganizationalUnitOutput[]
*/ */
#[Groups('user:write')] #[Groups('user:write')]
#[ApiProperty(readableLink: false, writableLink: false)] #[ApiProperty(readableLink: false, writableLink: false)]
@ -33,7 +34,7 @@ final class UserInput
public ?bool $enabled = true; public ?bool $enabled = true;
/** /**
* @var UserGroup[] * @var UserGroupOutput[]
*/ */
#[Groups('user:write')] #[Groups('user:write')]
#[ApiProperty(readableLink: false, writableLink: false)] #[ApiProperty(readableLink: false, writableLink: false)]
@ -62,8 +63,18 @@ final class UserInput
$this->username = $user->getUsername(); $this->username = $user->getUsername();
$this->enabled= $user->isEnabled(); $this->enabled= $user->isEnabled();
$this->userGroups = $user->getUserGroups()->toArray();
$this->allowedOrganizationalUnits = $user->getAllowedOrganizationalUnits()->toArray(); if ($user->getUserGroups()) {
foreach ($user->getUserGroups() as $userGroup) {
$this->userGroups[] = new UserGroupOutput($userGroup);
}
}
if ($user->getAllowedOrganizationalUnits()) {
foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) {
$this->allowedOrganizationalUnits[] = new OrganizationalUnitOutput($allowedOrganizationalUnit);
}
}
} }
public function createOrUpdateEntity(?User $user = null): User public function createOrUpdateEntity(?User $user = null): User

View File

@ -19,13 +19,19 @@ final class ClientOutput extends AbstractOutput
#[Groups(['client:read', 'organizational-unit:read'])] #[Groups(['client:read', 'organizational-unit:read'])]
public string $type = self::TYPE; public string $type = self::TYPE;
#[Groups(['client:read', 'organizational-unit:read'])]
public ?string $ip = '';
#[Groups(['client:read', 'organizational-unit:read'])]
public ?string $mac = '';
#[Groups(['client:read', 'organizational-unit:read'])] #[Groups(['client:read', 'organizational-unit:read'])]
public ?string $serialNumber = ''; public ?string $serialNumber = '';
#[Groups(['client:read'])] #[Groups(['client:read'])]
public ?string $netiface = ''; public ?string $netiface = '';
#[Groups(['client:read', 'organizational-unit:read'])] #[Groups(['client:read'])]
#[ApiProperty(readableLink: true)] #[ApiProperty(readableLink: true)]
public ?OrganizationalUnitOutput $organizationalUnit = null; public ?OrganizationalUnitOutput $organizationalUnit = null;
@ -51,6 +57,8 @@ final class ClientOutput extends AbstractOutput
$this->name = $client->getName(); $this->name = $client->getName();
$this->serialNumber = $client->getSerialNumber(); $this->serialNumber = $client->getSerialNumber();
$this->mac = $client->getMac();
$this->ip = $client->getIp();
$this->netiface = $client->getNetiface(); $this->netiface = $client->getNetiface();
if ($client->getOrganizationalUnit()) { if ($client->getOrganizationalUnit()) {

View File

@ -42,13 +42,19 @@ final class OrganizationalUnitOutput extends AbstractOutput
#[ApiProperty(readableLink: true)] #[ApiProperty(readableLink: true)]
public ?NetworkSettingsOutput $networkSettings = null; public ?NetworkSettingsOutput $networkSettings = null;
#[Groups(['organizational-unit:read'])]
public array $children = [];
#[Groups(['organizational-unit:read'])]
public array $clients = [];
#[Groups(['organizational-unit:read'])] #[Groups(['organizational-unit:read'])]
public \DateTime $createdAt; public \DateTime $createdAt;
#[Groups(['organizational-unit:read'])] #[Groups(['organizational-unit:read'])]
public ?string $createdBy = null; public ?string $createdBy = null;
public function __construct(OrganizationalUnit $organizationalUnit) public function __construct(OrganizationalUnit $organizationalUnit, array $context = [])
{ {
parent::__construct($organizationalUnit); parent::__construct($organizationalUnit);
@ -65,6 +71,18 @@ final class OrganizationalUnitOutput extends AbstractOutput
$this->parent = new self($organizationalUnit->getParent()); $this->parent = new self($organizationalUnit->getParent());
} }
if (isset($context['groups']) && in_array('organizational-unit:read', $context['groups'])) {
$this->children = $organizationalUnit->getOrganizationalUnits()->map(
fn(OrganizationalUnit $organizationalUnit) => new self($organizationalUnit, $context)
)->toArray();
}
if (isset($context['groups']) && in_array('organizational-unit:read', $context['groups'])) {
$this->clients = $organizationalUnit->getClients()->map(
fn(Client $client) => new ClientOutput($client)
)->toArray();
}
$this->path = $organizationalUnit->getPath(); $this->path = $organizationalUnit->getPath();
$this->createdAt = $organizationalUnit->getCreatedAt(); $this->createdAt = $organizationalUnit->getCreatedAt();
$this->createdBy = $organizationalUnit->getCreatedBy(); $this->createdBy = $organizationalUnit->getCreatedBy();

View File

@ -21,9 +21,9 @@ readonly class OrganizationalUnitChangeParentHandler
return throw new \InvalidArgumentException('The organizational unit has no parent.'); return throw new \InvalidArgumentException('The organizational unit has no parent.');
} }
foreach ($organizationalUnit->getOrganizationalUnits() as $child) { foreach ($organizationalUnit->getClients() as $client) {
$child->setParent($parent); $client->setOrganizationalUnit($parent);
$this->organizationalUnitRepository->save($child); $this->organizationalUnitRepository->save($client);
} }
$this->organizationalUnitRepository->delete($organizationalUnit); $this->organizationalUnitRepository->delete($organizationalUnit);

View File

@ -0,0 +1,48 @@
<?php
namespace App\Security\Voter;
use App\Dto\Output\OrganizationalUnitOutput;
use App\Entity\Client;
use App\Entity\OrganizationalUnit;
use App\Entity\User;
use App\Model\UserGroupPermissions;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class ClientVoter extends Voter
{
public const string VIEW = 'CLIENT_VIEW';
protected function supports(string $attribute, mixed $subject): bool
{
return $attribute === self::VIEW
&& $subject instanceof Client;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
/** @var User $user */
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
if (in_array(UserGroupPermissions::ROLE_SUPER_ADMIN, $user->getRoles())) {
return true;
}
if ($attribute === 'CLIENT_VIEW') {
foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) {
if ($allowedOrganizationalUnit->getId() === $subject->getOrganizationalUnit()->getEntity()->getId()) {
return true;
}
}
}
return false;
}
}

View File

@ -42,7 +42,7 @@ readonly class OrganizationalUnitProvider implements ProviderInterface
$items = new \ArrayObject(); $items = new \ArrayObject();
foreach ($paginator->getIterator() as $item){ foreach ($paginator->getIterator() as $item){
$items[] = new OrganizationalUnitOutput($item); $items[] = new OrganizationalUnitOutput($item, $context);
} }
return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems());
@ -56,7 +56,7 @@ readonly class OrganizationalUnitProvider implements ProviderInterface
throw new NotFoundHttpException('Organizational unit not found'); throw new NotFoundHttpException('Organizational unit not found');
} }
return new OrganizationalUnitOutput($item); return new OrganizationalUnitOutput($item, $context);
} }
public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null