diff --git a/config/api_platform/OperativeSystem.yaml b/config/api_platform/OperativeSystem.yaml index 737c8d0..988fed0 100644 --- a/config/api_platform/OperativeSystem.yaml +++ b/config/api_platform/OperativeSystem.yaml @@ -11,8 +11,8 @@ resources: ApiPlatform\Metadata\GetCollection: provider: App\State\Provider\OperativeSystemProvider filters: - - 'api_platform.filter.operative-system.order' - - 'api_platform.filter.operative-system.search' + - 'api_platform.filter.operative_system.order' + - 'api_platform.filter.operative_system.search' ApiPlatform\Metadata\Get: provider: App\State\Provider\OperativeSystemProvider diff --git a/config/api_platform/OrganizationalUnit.yaml b/config/api_platform/OrganizationalUnit.yaml index f161760..e03b552 100644 --- a/config/api_platform/OrganizationalUnit.yaml +++ b/config/api_platform/OrganizationalUnit.yaml @@ -7,13 +7,16 @@ resources: groups: ['default', 'organizational-unit:read'] denormalization_context: groups: ['organizational-unit:write'] - validation_context: - groups: ['organizational-unit:write'] operations: ApiPlatform\Metadata\GetCollection: provider: App\State\Provider\OrganizationalUnitProvider + filters: + - 'api_platform.filter.organizational_unit.order' + - 'api_platform.filter.organizational_unit.search' ApiPlatform\Metadata\Get: + security: 'is_granted("ORGANIZATIONAL_UNIT_VIEW", object)' provider: App\State\Provider\OrganizationalUnitProvider + securityMessage: 'Sorry, but you are not allowed to access this resource.' ApiPlatform\Metadata\Put: provider: App\State\Provider\OrganizationalUnitProvider ApiPlatform\Metadata\Patch: diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 6a6729c..67d91f3 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -4,7 +4,14 @@ api_platform: version: 1.0.0 path_segment_name_generator: 'api_platform.path_segment_name_generator.dash' formats: - jsonld: ['application/ld+json', 'application/json'] + jsonld: [ 'application/ld+json' ] + jsonhal: [ 'application/hal+json' ] + jsonapi: [ 'application/vnd.api+json' ] + json: [ 'application/json' ] + xml: [ 'application/xml', 'text/xml' ] + yaml: [ 'application/x-yaml' ] + csv: [ 'text/csv' ] + html: [ 'text/html' ] patch_formats: jsonld: ['application/ld+json', 'application/json'] mapping: diff --git a/config/services.yaml b/config/services.yaml index f454029..e9086eb 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -1,8 +1,3 @@ -# This file is the entry point to configure your own services. -# Files in the packages/ subdirectory configure your dependencies. - -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: services: @@ -11,8 +6,6 @@ services: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/' exclude: @@ -20,6 +13,11 @@ services: - '../src/Entity/' - '../src/Kernel.php' + App\Doctrine\UserAllowedOrganizationalUnitExtension: + tags: + - { name: api_platform.doctrine.orm.query_extension.collection } + - { name: api_platform.doctrine.orm.query_extension.item } + App\OpenApi\OpenApiFactory: decorates: 'api_platform.openapi.factory' arguments: [ '@App\OpenApi\OpenApiFactory.inner' ] diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index fd54ddb..f74ee52 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -41,6 +41,19 @@ services: tags: - [ 'api_platform.filter' ] + api_platform.filter.organizational_unit.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~, 'type': ~ } + $orderParameterName: 'order' + tags: + - [ 'api_platform.filter' ] + + api_platform.filter.organizational_unit.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'exact', 'type': 'exact' } ] + tags: + - [ 'api_platform.filter' ] api_platform.filter.partition.order: parent: 'api_platform.doctrine.orm.order_filter' diff --git a/migrations/Version20240620095914.php b/migrations/Version20240620095914.php new file mode 100644 index 0000000..5e4d7f2 --- /dev/null +++ b/migrations/Version20240620095914.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E419EB6921'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E419EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE SET NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E419EB6921'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E419EB6921 FOREIGN KEY (client_id) REFERENCES client (id)'); + } +} diff --git a/migrations/Version20240620100039.php b/migrations/Version20240620100039.php new file mode 100644 index 0000000..7aa27c5 --- /dev/null +++ b/migrations/Version20240620100039.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E419EB6921'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E419EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E419EB6921'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E419EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE SET NULL'); + } +} diff --git a/src/Doctrine/UserAllowedOrganizationalUnitExtension.php b/src/Doctrine/UserAllowedOrganizationalUnitExtension.php new file mode 100644 index 0000000..0aa9f25 --- /dev/null +++ b/src/Doctrine/UserAllowedOrganizationalUnitExtension.php @@ -0,0 +1,48 @@ +addWhere($queryBuilder, $resourceClass); + } + + public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void + { + $this->addWhere($queryBuilder, $resourceClass); + } + + private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void + { + /** @var User $user */ + $user = $this->security->getUser(); + + if (OrganizationalUnit::class !== $resourceClass || null === $user || in_array('ROLE_SUPER_ADMIN', $user->getRoles())) { + return; + } + $organizationalUnitIds = []; + foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) { + $organizationalUnitIds[] = $allowedOrganizationalUnit->getId(); + } + + $rootAlias = $queryBuilder->getRootAliases()[0]; + $queryBuilder->andWhere(sprintf('%s.id in (:ou)', $rootAlias)); + $queryBuilder->setParameter('ou', $organizationalUnitIds); + } +} \ No newline at end of file diff --git a/src/Entity/Partition.php b/src/Entity/Partition.php index c7af2c1..7795315 100644 --- a/src/Entity/Partition.php +++ b/src/Entity/Partition.php @@ -28,6 +28,7 @@ class Partition extends AbstractEntity private ?string $filesystem = null; #[ORM\ManyToOne(inversedBy: 'partitions')] + #[ORM\JoinColumn( onDelete: 'CASCADE')] private ?Client $client = null; #[ORM\Column] diff --git a/src/Entity/User.php b/src/Entity/User.php index 8e6e399..c186575 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -17,8 +17,8 @@ class User extends AbstractEntity implements UserInterface, PasswordAuthenticate { use ToggleableTrait; - const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN'; - const ROLE_USER = 'ROLE_USER'; + const string ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN'; + const string ROLE_USER = 'ROLE_USER'; #[ORM\Column(length: 180)] private ?string $username = null; diff --git a/src/Security/Voter/OrganizationalUnitVoter.php b/src/Security/Voter/OrganizationalUnitVoter.php new file mode 100644 index 0000000..1445a7f --- /dev/null +++ b/src/Security/Voter/OrganizationalUnitVoter.php @@ -0,0 +1,42 @@ +getUser(); + + // if the user is anonymous, do not grant access + if (!$user instanceof UserInterface) { + return false; + } + + if ($attribute === 'ORGANIZATIONAL_UNIT_VIEW' ) { + foreach ($user->getAllowedOrganizationalUnits() as $allowedOrganizationalUnit) { + if ($allowedOrganizationalUnit->getId() === $subject->getEntity()->getId()) { + return true; + } + } + } + + return false; + } +}