Browse Source

Merge branch 'v8-4328-security_revision' into release/2.4.beta

Olivier Massot 2 years ago
parent
commit
e0dbed6c5c
100 changed files with 1022 additions and 748 deletions
  1. 3 1
      .gitlab-ci.yml
  2. 27 27
      composer.json
  3. 0 26
      config/api_platform/Access/access.yaml
  4. 2 2
      config/services.yaml
  5. 3 0
      doc/internal_requests.md
  6. 186 0
      doc/security.md
  7. 1 14
      src/ApiResources/Access/AdminAccess.php
  8. 10 9
      src/ApiResources/Cotisation/Cotisation.php
  9. 37 7
      src/Doctrine/AbstractExtension.php
  10. 0 11
      src/Doctrine/Access/AccessExtensionInterface.php
  11. 15 0
      src/Doctrine/Access/AdditionalExtension/AdditionalAccessExtensionInterface.php
  12. 6 3
      src/Doctrine/Access/AdditionalExtension/DateTimeConstraintExtensionAdditional.php
  13. 7 4
      src/Doctrine/Access/AdditionalExtension/StudentsExtensionAdditional.php
  14. 6 32
      src/Doctrine/Access/CurrentAccessExtension.php
  15. 7 7
      src/Doctrine/Access/CurrentUserPersonalizedListExtension.php
  16. 7 6
      src/Doctrine/Billing/CurrentResidenceAreaExtension.php
  17. 7 6
      src/Doctrine/Booking/CurrentCoursesExtension.php
  18. 6 5
      src/Doctrine/Core/AllowedAddressPostalExtension.php
  19. 7 6
      src/Doctrine/Core/CurrentUserNotificationExtension.php
  20. 7 6
      src/Doctrine/Core/CurrentUserNotificationUserExtension.php
  21. 6 7
      src/Doctrine/Education/CurrentCycleExtension.php
  22. 7 6
      src/Doctrine/Education/CurrentEducationNotationConfigExtension.php
  23. 7 6
      src/Doctrine/Education/CurrentEducationTimingExtension.php
  24. 7 6
      src/Doctrine/Network/CurrentNetworkOrganizationExtension.php
  25. 7 6
      src/Doctrine/Organization/CurrentOrganizationAddressPostalExtension.php
  26. 7 6
      src/Doctrine/Organization/CurrentOrganizationArticleExtension.php
  27. 6 5
      src/Doctrine/Organization/CurrentOrganizationExtension.php
  28. 6 1
      src/Entity/Access/Access.php
  29. 2 8
      src/Entity/Access/AccessFamily.php
  30. 1 1
      src/Entity/Access/OrganizationFunction.php
  31. 6 1
      src/Entity/Access/PersonalizedList.php
  32. 2 8
      src/Entity/Billing/AccessBilling.php
  33. 1 1
      src/Entity/Billing/AccessIntangible.php
  34. 1 7
      src/Entity/Billing/AccessPayer.php
  35. 2 8
      src/Entity/Billing/Bill.php
  36. 1 7
      src/Entity/Billing/BillAccounting.php
  37. 2 8
      src/Entity/Billing/BillCredit.php
  38. 2 8
      src/Entity/Billing/BillLine.php
  39. 1 7
      src/Entity/Billing/BillingIntangibleExcludeDate.php
  40. 1 5
      src/Entity/Billing/BillingSetting.php
  41. 2 8
      src/Entity/Billing/EducationalProjectPayer.php
  42. 2 8
      src/Entity/Billing/FamilyQuotient.php
  43. 5 18
      src/Entity/Billing/ResidenceArea.php
  44. 2 8
      src/Entity/Booking/Attendance.php
  45. 1 7
      src/Entity/Booking/AttendanceBooking.php
  46. 5 9
      src/Entity/Booking/Course.php
  47. 2 8
      src/Entity/Booking/EducationalProject.php
  48. 1 7
      src/Entity/Booking/Event.php
  49. 2 8
      src/Entity/Booking/EventUser.php
  50. 2 8
      src/Entity/Booking/Examen.php
  51. 2 8
      src/Entity/Booking/OrganizationHoliday.php
  52. 2 8
      src/Entity/Booking/PersonHoliday.php
  53. 1 1
      src/Entity/Core/AbstractInformation.php
  54. 7 8
      src/Entity/Core/AddressPostal.php
  55. 4 15
      src/Entity/Core/BankAccount.php
  56. 4 15
      src/Entity/Core/ContactPoint.php
  57. 46 6
      src/Entity/Core/File.php
  58. 4 1
      src/Entity/Core/Notification.php
  59. 3 0
      src/Entity/Core/NotificationUser.php
  60. 2 8
      src/Entity/Core/Tagg.php
  61. 1 10
      src/Entity/Core/Tips.php
  62. 1 7
      src/Entity/Donor/Donor.php
  63. 2 8
      src/Entity/Education/CriteriaNotation.php
  64. 4 14
      src/Entity/Education/Cycle.php
  65. 2 8
      src/Entity/Education/Education.php
  66. 2 8
      src/Entity/Education/EducationCategory.php
  67. 1 7
      src/Entity/Education/EducationCurriculum.php
  68. 2 8
      src/Entity/Education/EducationNotation.php
  69. 5 14
      src/Entity/Education/EducationNotationConfig.php
  70. 2 8
      src/Entity/Education/EducationStudent.php
  71. 2 8
      src/Entity/Education/EducationTeacher.php
  72. 5 18
      src/Entity/Education/EducationTiming.php
  73. 1 7
      src/Entity/Network/Network.php
  74. 4 13
      src/Entity/Network/NetworkOrganization.php
  75. 2 8
      src/Entity/Organization/Activity.php
  76. 2 8
      src/Entity/Organization/Jury.php
  77. 6 13
      src/Entity/Organization/Organization.php
  78. 7 16
      src/Entity/Organization/OrganizationAddressPostal.php
  79. 5 12
      src/Entity/Organization/OrganizationArticle.php
  80. 1 1
      src/Entity/Organization/OrganizationLicence.php
  81. 1 1
      src/Entity/Organization/Settings.php
  82. 8 13
      src/Entity/Organization/Subdomain.php
  83. 2 10
      src/Entity/Organization/TypeOfPractice.php
  84. 2 8
      src/Entity/Person/Commission.php
  85. 1 7
      src/Entity/Person/CommissionMember.php
  86. 2 8
      src/Entity/Person/CompanyPerson.php
  87. 1 7
      src/Entity/Person/Person.php
  88. 1 1
      src/Entity/Person/PersonActivity.php
  89. 1 7
      src/Entity/Person/PersonAddressPostal.php
  90. 1 7
      src/Entity/Place/Place.php
  91. 2 8
      src/Entity/Place/Room.php
  92. 2 8
      src/Entity/Product/Equipment.php
  93. 1 7
      src/Entity/Product/EquipmentLoan.php
  94. 2 8
      src/Entity/Product/Intangible.php
  95. 2 8
      src/Entity/Product/IntangibleDiscountDetail.php
  96. 2 0
      src/Enum/Core/FileTypeEnum.php
  97. 19 0
      src/Enum/Core/FileVisibilityEnum.php
  98. 187 0
      src/Security/Voter/AbstractVoter.php
  99. 208 0
      src/Security/Voter/Core/FileVoter.php
  100. 2 1
      src/Security/Voter/SwitchUserVoter.php

+ 3 - 1
.gitlab-ci.yml

@@ -16,7 +16,9 @@ before_script:
 
 
 static_analysis:
 static_analysis:
   script:
   script:
-    - php --version && php -d memory_limit=512M vendor/bin/phpstan analyse -c phpstan.neon.dist --error-format gitlab > phpstan.json
+    - php --version
+    - php -d memory_limit=512M vendor/bin/phpstan analyse -c phpstan.neon.dist --error-format gitlab > phpstan.json  # Display code quality in MR
+    - php -d memory_limit=512M vendor/bin/phpstan analyse -c phpstan.neon.dist  # Display in console
   artifacts:
   artifacts:
     when: always
     when: always
     reports:
     reports:

+ 27 - 27
composer.json

@@ -32,33 +32,33 @@
         "phpdocumentor/reflection-docblock": "^5.2",
         "phpdocumentor/reflection-docblock": "^5.2",
         "ramsey/uuid": "^4.2",
         "ramsey/uuid": "^4.2",
         "ramsey/uuid-doctrine": "^2.0",
         "ramsey/uuid-doctrine": "^2.0",
-        "symfony/asset": "6.2.*",
-        "symfony/console": "6.2.*",
-        "symfony/doctrine-messenger": "6.2.*",
-        "symfony/dotenv": "6.2.*",
-        "symfony/error-handler": "6.2.*",
-        "symfony/expression-language": "6.2.*",
+        "symfony/asset": "6.3.*",
+        "symfony/console": "6.3.*",
+        "symfony/doctrine-messenger": "6.3.*",
+        "symfony/dotenv": "6.3.*",
+        "symfony/error-handler": "6.3.*",
+        "symfony/expression-language": "6.3.*",
         "symfony/flex": "^1.3.1",
         "symfony/flex": "^1.3.1",
-        "symfony/framework-bundle": "6.2.*",
-        "symfony/http-client": "6.2.*",
-        "symfony/intl": "6.2.*",
-        "symfony/lock": "6.2.*",
-        "symfony/mailer": "6.2.*",
+        "symfony/framework-bundle": "6.3.*",
+        "symfony/http-client": "6.3.*",
+        "symfony/intl": "6.3.*",
+        "symfony/lock": "6.3.*",
+        "symfony/mailer": "6.3.*",
         "symfony/mercure": "^0.6.1",
         "symfony/mercure": "^0.6.1",
         "symfony/mercure-bundle": "^0.3.4",
         "symfony/mercure-bundle": "^0.3.4",
-        "symfony/messenger": "6.2.*",
+        "symfony/messenger": "6.3.*",
         "symfony/monolog-bundle": "^3.7",
         "symfony/monolog-bundle": "^3.7",
         "symfony/polyfill-intl-icu": "^1.21",
         "symfony/polyfill-intl-icu": "^1.21",
         "symfony/polyfill-intl-messageformatter": "^1.24",
         "symfony/polyfill-intl-messageformatter": "^1.24",
-        "symfony/property-access": "6.2.*",
-        "symfony/property-info": "6.2.*",
-        "symfony/security-bundle": "6.2.*",
-        "symfony/serializer": "6.2.*",
-        "symfony/translation": "6.2.*",
-        "symfony/twig-bundle": "6.2.*",
-        "symfony/uid": "6.2.*",
-        "symfony/validator": "6.2.*",
-        "symfony/yaml": "6.2.*",
+        "symfony/property-access": "6.3.*",
+        "symfony/property-info": "6.3.*",
+        "symfony/security-bundle": "6.3.*",
+        "symfony/serializer": "6.3.*",
+        "symfony/translation": "6.3.*",
+        "symfony/twig-bundle": "6.3.*",
+        "symfony/uid": "6.3.*",
+        "symfony/validator": "6.3.*",
+        "symfony/yaml": "6.3.*",
         "twig/cssinliner-extra": "^3.4",
         "twig/cssinliner-extra": "^3.4",
         "twig/extra-bundle": "^3.4",
         "twig/extra-bundle": "^3.4",
         "twig/inky-extra": "^3.4",
         "twig/inky-extra": "^3.4",
@@ -79,13 +79,13 @@
         "phpstan/phpstan-symfony": "^1.2",
         "phpstan/phpstan-symfony": "^1.2",
         "phpunit/phpunit": "^9.6",
         "phpunit/phpunit": "^9.6",
         "rector/rector": "^0.15.13",
         "rector/rector": "^0.15.13",
-        "symfony/browser-kit": "6.2.*",
-        "symfony/css-selector": "6.2.*",
-        "symfony/debug-bundle": "6.2.*",
+        "symfony/browser-kit": "6.3.*",
+        "symfony/css-selector": "6.3.*",
+        "symfony/debug-bundle": "6.3.*",
         "symfony/maker-bundle": "^1.21",
         "symfony/maker-bundle": "^1.21",
         "symfony/phpunit-bridge": "^6.2",
         "symfony/phpunit-bridge": "^6.2",
-        "symfony/stopwatch": "6.2.*",
-        "symfony/web-profiler-bundle": "6.2.*",
+        "symfony/stopwatch": "6.3.*",
+        "symfony/web-profiler-bundle": "6.3.*",
         "timeweb/phpstan-enum": "^3.1",
         "timeweb/phpstan-enum": "^3.1",
         "zenstruck/foundry": "^1.31"
         "zenstruck/foundry": "^1.31"
     },
     },
@@ -139,7 +139,7 @@
     "extra": {
     "extra": {
         "symfony": {
         "symfony": {
             "allow-contrib": false,
             "allow-contrib": false,
-            "require": "6.2.*"
+            "require": "6.3.*"
         },
         },
         "phpstan": {
         "phpstan": {
             "includes": [
             "includes": [

+ 0 - 26
config/api_platform/Access/access.yaml

@@ -1,35 +1,9 @@
 resources:
 resources:
   App\Entity\Access\Access:
   App\Entity\Access\Access:
     - operations:
     - operations:
-        ApiPlatform\Metadata\GetCollection: ~
-
         ApiPlatform\Metadata\Get:
         ApiPlatform\Metadata\Get:
           security: '(is_granted("ROLE_USERS_VIEW") and object.getOrganization().getId() == user.getOrganization().getId()) or (object.getId() == user.getId())'
           security: '(is_granted("ROLE_USERS_VIEW") and object.getOrganization().getId() == user.getOrganization().getId()) or (object.getId() == user.getId())'
 
 
         ApiPlatform\Metadata\Put:
         ApiPlatform\Metadata\Put:
           security: 'is_granted("ROLE_USERS") or (object.getId() == user.getId())'
           security: 'is_granted("ROLE_USERS") or (object.getId() == user.getId())'
 
 
-        ApiPlatform\Metadata\Delete: ~
-
-    - operations:
-        ApiPlatform\Metadata\GetCollection:
-          name: 'cget_students'
-          uriTemplate: '/students'
-          security: 'is_granted("ROLE_USERS_VIEW")'
-
-    - operations:
-        ApiPlatform\Metadata\GetCollection:
-          name: 'cget_access_person_ref'
-          uriTemplate: '/access_people'
-          normalization_context:
-            groups: [ 'access_people_ref' ]
-
-    - operations:
-        ApiPlatform\Metadata\Get:
-          name: 'get_access_address'
-          uriTemplate: '/access_addresses/{id}'
-          requirements:
-            id: '\d+'
-          normalization_context:
-            groups: [ 'access_address', 'address' ] ]
-          security: 'object.getOrganization().getId() == user.getOrganization().getId()'

+ 2 - 2
config/services.yaml

@@ -48,11 +48,11 @@ services:
     # To use the test fixtures
     # To use the test fixtures
     App\Tests\Fixture\:
     App\Tests\Fixture\:
         resource: '%kernel.project_dir%/tests/Fixture/*'
         resource: '%kernel.project_dir%/tests/Fixture/*'
-        
+
     #########################################
     #########################################
     ##  TAG Services ##
     ##  TAG Services ##
     _instanceof:
     _instanceof:
-        App\Doctrine\Access\AccessExtensionInterface:
+        App\Doctrine\Access\AdditionalExtension\AdditionalAccessExtensionInterface:
             tags: ['app.extensions.access']
             tags: ['app.extensions.access']
         App\Service\Access\OptionalsRolesInterface:
         App\Service\Access\OptionalsRolesInterface:
             tags: ['app.optionalsroles']
             tags: ['app.optionalsroles']

+ 3 - 0
doc/internal_requests.md

@@ -46,6 +46,9 @@ Les appels à cette route ne sont autorisés que si :
 
 
 Si ces deux conditions ne sont pas remplies, la requête est rejetée, et ce même si l'utilisateur est authentifié.
 Si ces deux conditions ne sont pas remplies, la requête est rejetée, et ce même si l'utilisateur est authentifié.
 
 
+Les routes internal sont configurées ici : `config/packages/security.yaml`
+
+
 
 
 ### Valider le fonctionnement
 ### Valider le fonctionnement
 
 

+ 186 - 0
doc/security.md

@@ -0,0 +1,186 @@
+# Security
+
+## Authentification Symfony
+
+### Fonctionnement de base
+
+L'authentification se fait via une requête POST envoyée à l'adresse `/login_check` avec le body suivant : 
+
+    {
+        "username": "login",
+        "password": "password"
+    }
+
+En cas de succès, la requête renvoie un token qui servira ensuite à l'utilisateur à s'identifier.
+
+Les requêtes suivantes devront posséder les headers suivants : 
+
+* `x-accessid` : l'id de l'utilisateur (ou Access)
+* `authorization` : une chaine de caractères de la forme "BEARER XXXXX", où XXXXX est le token retourné par la requête de login
+
+### Connexion Switch
+
+Certains utilisateurs (admins, familles) peuvent prendre le rôle d'un autre utilisateur via la connexion switch.
+
+Pour ce faire, un nouveau header doit être ajouté aux requêtes : 
+
+* `x-switch-user`: l'id de l'utilisateur dont on veut prendre le rôle
+
+
+
+## Roles et Modules
+
+Les droits d'un utilisateur sont conditionnés à différents critères, dont : 
+
+* Les **modules** que possède l'organisation à laquelle il est appartient
+* Les **rôles** de cet utilisateur au sein de cette organisation
+
+On peut obtenir la liste des modules de l'organisation et des rôles de l'utilisateur actif en son sein au moyen de la 
+requête : `/api/my_profile`
+
+
+### Modules
+
+Les modules d'une organisation dépendent du produit acheté par celle-ci et des éventuels modules complémentaires. Ces deux 
+informations sont stoquées dans la table `Settings`.
+
+Le fichier `config/opentalent/products.yaml` définit :
+
+- L'appartenance des _modules_ aux _products_
+- L'appartenance des _entities_ aux _modules_
+
+De plus, le fichier `config/opentalent/modulesbyconditions.yaml` complète cette configuration en définissant des modules
+présentant des conditions particulières (appartenance à la CMF en particulier)
+
+A chaque requête effectuée, la classe `\App\Security\Voter\ModuleVoter` vérifie si la ressource demandée appartient à un
+module possédé par l'organisation de l'utilisateur. Si ce n'est pas le cas, une erreur `AccessDeniedHttpException` est levée.
+
+
+## Différentes méthodes de sécurisation
+
+### Les annotations Api-Platform
+
+La sécurité des ApiResources peut être définie de manière globale pour la ressource, ou pour chaque opération (Get, 
+GetCollection, Put, Post, Delete) via les annotations.
+
+Exemple : 
+
+    #[ApiResource(
+        operations: [
+            new Get(
+                security: '(is_granted("ROLE_ORGANIZATION_VIEW") or is_granted("ROLE_ORGANIZATION")) and object.getOrganization().getId() == user.getOrganization().getId()'
+            ),
+            new Put(
+                security: 'is_granted("ROLE_ORGANIZATION") and object.getOrganization().getId() == user.getOrganization().getId()'
+            )
+        ],
+    )]
+
+Dans certains cas plus complexes (ex: Access), cette configuration peut être déplacée dans un fichier de configuration
+situé dans le répertoire `~/config/api_platform/` et portant le nom de la ressource.
+
+> Voir plus : https://api-platform.com/docs/core/security/
+
+
+### Voters
+
+Les voters permettent de contrôler l'accès à certaines ressources, selon le type d'opération.
+
+Ils implémentent essentiellement deux méthodes : `supports` et `voteOnAttribute` qui prennent en paramètres la ressource 
+et le type d'opération. `supports` retourne `true` si le voter doit s'appliquer dans ce cas. Si oui, `voteOnAttribute`
+est appellée et retourne `true` si l'opération est autorisée.
+
+> TODO: quand faut-il retourner false et quand faut-il lever une AccessDeniedHttpException ?
+
+Pour qu'un Voter soit appellé, il faut configurer la sécurité de la ressource via son annotation et utiliser la méthode 
+`is_granted` combinée à une des constantes : 
+
+Exemple : 
+
+    #[ApiResource(
+        operations: [
+            new Get(security: "is_granted('READ', object)"),
+            new Put(security: "is_granted('EDIT', object)"),
+            new Post(security: "is_granted('CREATE', object)"),
+            new Delete(security: "is_granted('DELETE', object)"),
+        ]
+    )]
+    class File
+
+    
+
+Les voters custom sont enregistrés dans le répertoire `src/Security/Voter`.
+
+> Voir plus : https://symfony.com/doc/current/security/voters.html
+
+
+### Extensions Doctrine
+
+Le framework Api-Platform propose un système d'extensions doctrine pour ajouter des filtres automatiques aux opérations
+réalisées sur des ressources voulues.
+
+Concrètement, l'extension ajoutera une condition au `WHERE` de la requête SQL.
+
+Une extension implémente trois méthodes : 
+
+* `applyToCollection`
+* `applyToItem`
+* `addWhere`
+
+Les deux premières permettent de tester si l'extension doit s'appliquer à la ressource.
+
+La dernière ajoute la condition en question.
+
+> Voir : https://api-platform.com/docs/core/extensions/
+
+
+### Quand utiliser les annotations, un Voter ou une Extension Doctrine ?
+
+Si la sécurité doit pouvoir s'appliquer à une collection et filtrer le résultat de celle-ci (exemple: les notifications 
+d'un utilisateur) : on utilisera une _Extension Doctrine_
+
+Sinon, si les conditions d'accès peuvent s'écrire facilement avec l'expression language des annotations
+api-platform, on utilisera les _annotations Api-platform_.
+
+Si aucune des conditions précédentes n'est remplie, on utilisera un _Voter Symfony_.
+
+
+## Internal Requests
+
+Certaines routes de la forme `/internal/...` permettent d'accepter des requêtes sans que le client ne
+soit authentifié (cf `config/packages/security.yaml`), en se basant sur un contrôle de l'IP et un token. 
+
+> Voir: https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/doc/internal_requests.md
+
+
+
+## Cas particuliers 
+
+### Les Fichiers
+
+Le client est autorisé à consulter l'enregistrement si l'une de ces conditions est vraie :
+
+* pas de date de disponibilité ou date de disponibilité antérieure à Now
+* date de disponibilité postérieure à Now, mais utilisateur a le rôle ROLE_BILLACCOUNTING et le fichier est de type BILL (facture)
+* visibilité est 'EVERYBODY' 
+* requête interne
+* est connecté et fait partie des AccessPersons du fichier
+* est connecté et est le propriétaire du fichier
+* est connecté et a le rôle ROLE_FILE et fichier appartient à l'utilisateur ou à son organisation
+* est connecté et fichier a des accessRoles et user a un des roles présents dans accessRoles et fichier appartient à l'utilisateur ou à son organisation (deprecated)
+
+
+Est autorisé à éditer si :
+
+* pas de date de disponibilité ou date de disponibilité antérieure à Now
+* date de disponibilité postérieure à Now, mais utilisateur a le rôle ROLE_BILLACCOUNTING et le fichier est de type BILL (facture)
+* est connecté et a le rôle ROLE_FILE et fichier appartient à l'utilisateur ou à son organisation
+* est connecté et est le propriétaire du fichier
+
+Est autorisé à créer si :
+
+* est connecté
+
+Est autorisé à supprimer si :
+
+* est connecté et est autorisé à éditer

+ 1 - 14
src/ApiResources/Access/AdminAccess.php

@@ -15,20 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Classe resource qui contient les champs d'un compte admin
  * Classe resource qui contient les champs d'un compte admin
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            uriTemplate: '/admin/{id}',
-            defaults: ['id' => 0],
-            provider: AdminAccessProvider::class
-        ),
-        new Put(
-            uriTemplate: '/admin/{id}',
-            defaults: ['id' => 0],
-            processor: AdminAccessProcessor::class
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 class AdminAccess implements ApiResourcesInterface
 class AdminAccess implements ApiResourcesInterface
 {
 {
     #[ApiProperty(identifier: true)]
     #[ApiProperty(identifier: true)]

+ 10 - 9
src/ApiResources/Cotisation/Cotisation.php

@@ -12,16 +12,17 @@ use Symfony\Component\Validator\Constraints as Assert;
 
 
 /**
 /**
  * Classe resource qui contient les informations des cotisations de la 5.9
  * Classe resource qui contient les informations des cotisations de la 5.9
+ *
+ * Security :
+ *   * @see App\Security\Voter\CotisationVoter
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            uriTemplate: '/cotisations/{organizationId}',
-            security: 'is_granted("ROLE_COTISATION", object) and object.getOrganizationId() == user.getOrganization().getId()',
-            provider: CotisationProvider::class
-        )
-    ]
-)]
+#[ApiResource(operations: [
+    new Get(
+        uriTemplate: '/cotisations/{organizationId}',
+        security: 'is_granted("ROLE_COTISATION", object) and object.getOrganizationId() == user.getOrganization().getId()',
+        provider: CotisationProvider::class
+    )
+])]
 class Cotisation implements ApiResourcesInterface
 class Cotisation implements ApiResourcesInterface
 {
 {
     #[ApiProperty(identifier: true)]
     #[ApiProperty(identifier: true)]

+ 37 - 7
src/Doctrine/AbstractExtension.php

@@ -11,11 +11,13 @@ use Doctrine\ORM\QueryBuilder;
 use Exception;
 use Exception;
 
 
 /**
 /**
- * Classe ... qui ...
+ * Base class for custom doctrine extensions
  */
  */
 abstract class AbstractExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
 abstract class AbstractExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
 {
 {
     /**
     /**
+     * Called by doctrine when getting a collection
+     *
      * @param QueryBuilder $queryBuilder
      * @param QueryBuilder $queryBuilder
      * @param QueryNameGeneratorInterface $queryNameGenerator
      * @param QueryNameGeneratorInterface $queryNameGenerator
      * @param string $resourceClass
      * @param string $resourceClass
@@ -26,10 +28,12 @@ abstract class AbstractExtension implements QueryCollectionExtensionInterface, Q
      */
      */
     public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
     public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
     {
     {
-        $this->addWhere($queryBuilder, $resourceClass);
+        $this->apply($queryBuilder, $resourceClass, $operation);
     }
     }
 
 
     /**
     /**
+     * Called by doctrine when getting an item
+     *
      * @param QueryBuilder $queryBuilder
      * @param QueryBuilder $queryBuilder
      * @param QueryNameGeneratorInterface $queryNameGenerator
      * @param QueryNameGeneratorInterface $queryNameGenerator
      * @param string $resourceClass
      * @param string $resourceClass
@@ -41,11 +45,37 @@ abstract class AbstractExtension implements QueryCollectionExtensionInterface, Q
      */
      */
     public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
     public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
     {
     {
-        $this->addWhere($queryBuilder, $resourceClass);
+        $this->apply($queryBuilder, $resourceClass, $operation);
     }
     }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        throw new Exception('need override addWhere function', 500);
+    /**
+     * Generic application of the extension
+     *
+     * @param $queryBuilder
+     * @param $resourceClass
+     * @return void
+     */
+    protected function apply(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void {
+        if (!$this->supports($resourceClass, $operation)) {
+            return;
+        }
+        $this->addWhere($queryBuilder, $resourceClass, $operation);
     }
     }
-}
+
+    /**
+     * Returns true if the extension supports the given resource
+     *
+     * @param string $resourceClass
+     * @return bool
+     */
+    abstract protected function supports(string $resourceClass, ?Operation $operation): bool;
+
+    /**
+     * Add one or more filters to the query
+     *
+     * @param QueryBuilder $queryBuilder
+     * @param string $resourceClass
+     * @return void
+     */
+    abstract protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void;
+}

+ 0 - 11
src/Doctrine/Access/AccessExtensionInterface.php

@@ -1,11 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace App\Doctrine\Access;
-
-use Doctrine\ORM\QueryBuilder;
-
-interface AccessExtensionInterface{
-    public function support(string $name): bool;
-    public function addWhere(QueryBuilder $queryBuilder): void;
-}

+ 15 - 0
src/Doctrine/Access/AdditionalExtension/AdditionalAccessExtensionInterface.php

@@ -0,0 +1,15 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Doctrine\Access\AdditionalExtension;
+
+use Doctrine\ORM\QueryBuilder;
+
+/**
+ * Complément d'extension pour CurrentAccessExtension.
+ * Les classes implémentant cette interface seront appellées par le service CurrentAccessExtensionIterator
+ */
+interface AdditionalAccessExtensionInterface{
+    public function support(string $name): bool;
+    public function addWhere(QueryBuilder $queryBuilder): void;
+}

+ 6 - 3
src/Doctrine/Access/Extensions/DateTimeConstraintExtension.php → src/Doctrine/Access/AdditionalExtension/DateTimeConstraintExtensionAdditional.php

@@ -1,13 +1,16 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
 
 
-namespace App\Doctrine\Access\Extensions;
+namespace App\Doctrine\Access\AdditionalExtension;
 
 
-use App\Doctrine\Access\AccessExtensionInterface;
 use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\QueryBuilder;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 
-class DateTimeConstraintExtension implements AccessExtensionInterface {
+/**
+ * Contrainte supplémentaire pour CurrentAccessExtension.
+ * Ajoute un critère de dates
+ */
+class DateTimeConstraintExtensionAdditional implements AdditionalAccessExtensionInterface {
     public function __construct(
     public function __construct(
         private RequestStack $requestStack
         private RequestStack $requestStack
     ){
     ){

+ 7 - 4
src/Doctrine/Access/Extensions/StudentsExtension.php → src/Doctrine/Access/AdditionalExtension/StudentsExtensionAdditional.php

@@ -1,12 +1,15 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
 
 
-namespace App\Doctrine\Access\Extensions;
+namespace App\Doctrine\Access\AdditionalExtension;
 
 
-use App\Doctrine\Access\AccessExtensionInterface;
 use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\QueryBuilder;
 
 
-class StudentsExtension implements AccessExtensionInterface {
+/**
+ * Contrainte supplémentaire pour CurrentAccessExtension.
+ *
+ */
+class StudentsExtensionAdditional implements AdditionalAccessExtensionInterface {
 
 
     public function support(string $name): bool
     public function support(string $name): bool
     {
     {
@@ -16,4 +19,4 @@ class StudentsExtension implements AccessExtensionInterface {
     public function addWhere(QueryBuilder $queryBuilder): void
     public function addWhere(QueryBuilder $queryBuilder): void
     {
     {
     }
     }
-}
+}

+ 6 - 32
src/Doctrine/Access/CurrentAccessExtension.php

@@ -7,6 +7,7 @@ use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
 use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
 use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
 use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
 use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
 use ApiPlatform\Metadata\Operation;
 use ApiPlatform\Metadata\Operation;
+use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
 use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
 use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\QueryBuilder;
@@ -16,7 +17,7 @@ use Symfony\Bundle\SecurityBundle\Security;
  * Class CurrentAccessExtension : Filtre de sécurité par défaut pour une resource Access
  * Class CurrentAccessExtension : Filtre de sécurité par défaut pour une resource Access
  * @package App\Doctrine\Access
  * @package App\Doctrine\Access
  */
  */
-final class CurrentAccessExtension  implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
+final class CurrentAccessExtension extends AbstractExtension
 {
 {
     public function __construct(
     public function __construct(
         private Security $security,
         private Security $security,
@@ -24,39 +25,12 @@ final class CurrentAccessExtension  implements QueryCollectionExtensionInterface
     )
     )
     { }
     { }
 
 
-    /**
-     * @param QueryBuilder $queryBuilder
-     * @param QueryNameGeneratorInterface $queryNameGenerator
-     * @param string $resourceClass
-     * @param Operation|null $operation
-     * @param mixed[] $context
-     * @return void
-     */
-    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
-    {
-        $this->addWhere($queryBuilder, $resourceClass, $operation?->getName());
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === Access::class;
     }
     }
 
 
-    /**
-     * @param QueryBuilder $queryBuilder
-     * @param QueryNameGeneratorInterface $queryNameGenerator
-     * @param string $resourceClass
-     * @param list<int> $identifiers
-     * @param Operation|null $operation
-     * @param mixed[] $context
-     * @return void
-     */
-    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
+    public function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
     {
     {
-        $this->addWhere($queryBuilder, $resourceClass, $operation?->getName());
-    }
-
-    public function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?string $operationName): void
-    {
-        if (Access::class !== $resourceClass) {
-            return;
-        }
-
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -65,6 +39,6 @@ final class CurrentAccessExtension  implements QueryCollectionExtensionInterface
             ->setParameter('current_organization', $currentUser->getOrganization())
             ->setParameter('current_organization', $currentUser->getOrganization())
         ;
         ;
 
 
-        $this->currentAccessExtensionIterator->addWhere($queryBuilder, $operationName);
+        $this->currentAccessExtensionIterator->addWhere($queryBuilder, $operation?->getName());
     }
     }
 }
 }

+ 7 - 7
src/Doctrine/Access/CurrentUserPersonalizedListExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Access;
 namespace App\Doctrine\Access;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\PersonalizedList;
 use App\Entity\Access\PersonalizedList;
 use Doctrine\ORM\QueryBuilder;
 use Doctrine\ORM\QueryBuilder;
@@ -17,16 +18,15 @@ final class CurrentUserPersonalizedListExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (PersonalizedList::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === PersonalizedList::class;
+    }
 
 
-        /** @var PersonalizedList $currentUser */
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $queryBuilder->andWhere(sprintf('%s.access = :current_access', $rootAlias));
         $queryBuilder->andWhere(sprintf('%s.access = :current_access', $rootAlias));
         $queryBuilder->setParameter('current_access', $currentUser);
         $queryBuilder->setParameter('current_access', $currentUser);
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Billing/CurrentResidenceAreaExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Billing;
 namespace App\Doctrine\Billing;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Billing\ResidenceArea;
 use App\Entity\Billing\ResidenceArea;
@@ -18,12 +19,12 @@ final class CurrentResidenceAreaExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (ResidenceArea::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === ResidenceArea::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentResidenceAreaExtension extends AbstractExtension
             ->setParameter('billingSetting', $currentUser->getOrganization()->getBillingSetting())
             ->setParameter('billingSetting', $currentUser->getOrganization()->getBillingSetting())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Booking/CurrentCoursesExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Booking;
 namespace App\Doctrine\Booking;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Booking\Course;
 use App\Entity\Booking\Course;
@@ -18,15 +19,15 @@ final class CurrentCoursesExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === Course::class;
+    }
+
     /**
     /**
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      */
      */
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
     {
     {
-        if (Course::class !== $resourceClass) {
-            return;
-        }
-
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -37,4 +38,4 @@ final class CurrentCoursesExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 6 - 5
src/Doctrine/Core/AllowedAddressPostalExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Core;
 namespace App\Doctrine\Core;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Core\AddressPostal;
 use App\Entity\Core\AddressPostal;
@@ -18,12 +19,12 @@ final class AllowedAddressPostalExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (AddressPostal::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === AddressPostal::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];

+ 7 - 6
src/Doctrine/Core/CurrentUserNotificationExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Core;
 namespace App\Doctrine\Core;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Core\Notification;
 use App\Entity\Core\Notification;
@@ -10,7 +11,7 @@ use Doctrine\ORM\QueryBuilder;
 use Symfony\Bundle\SecurityBundle\Security;
 use Symfony\Bundle\SecurityBundle\Security;
 
 
 /**
 /**
- * Class NotificationExtension : Filtre de sécurité par défaut pour une resource Notification
+ * Filtre de sécurité par défaut pour une resource Notification
  * @package App\Doctrine\Core
  * @package App\Doctrine\Core
  */
  */
 final class CurrentUserNotificationExtension extends AbstractExtension
 final class CurrentUserNotificationExtension extends AbstractExtension
@@ -18,15 +19,15 @@ final class CurrentUserNotificationExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === Notification::class;
+    }
+
     /**
     /**
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      */
      */
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
     {
     {
-        if (Notification::class !== $resourceClass) {
-            return;
-        }
-
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];

+ 7 - 6
src/Doctrine/Core/CurrentNotificationUserExtension.php → src/Doctrine/Core/CurrentUserNotificationUserExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Core;
 namespace App\Doctrine\Core;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Core\NotificationUser;
 use App\Entity\Core\NotificationUser;
@@ -14,20 +15,20 @@ use Symfony\Bundle\SecurityBundle\Security;
  *
  *
  * @package App\Doctrine\Core
  * @package App\Doctrine\Core
  */
  */
-final class CurrentNotificationUserExtension extends AbstractExtension
+final class CurrentUserNotificationUserExtension extends AbstractExtension
 {
 {
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === NotificationUser::class;
+    }
+
     /**
     /**
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      * @todo : A la suite de la migration, il faut supprimer le where avec le discr.
      */
      */
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
     {
     {
-        if (NotificationUser::class !== $resourceClass) {
-            return;
-        }
-
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];

+ 6 - 7
src/Doctrine/Education/CurrentCycleExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Education;
 namespace App\Doctrine\Education;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Education\Cycle;
 use App\Entity\Education\Cycle;
@@ -18,13 +19,11 @@ final class CurrentCycleExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === Cycle::class;
+    }
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
     {
     {
-        if (Cycle::class !== $resourceClass) {
-            return;
-        }
-
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -33,4 +32,4 @@ final class CurrentCycleExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Education/CurrentEducationNotationConfigExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Education;
 namespace App\Doctrine\Education;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Education\EducationNotationConfig;
 use App\Entity\Education\EducationNotationConfig;
@@ -18,12 +19,12 @@ final class CurrentEducationNotationConfigExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (EducationNotationConfig::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === EducationNotationConfig::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentEducationNotationConfigExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Education/CurrentEducationTimingExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Education;
 namespace App\Doctrine\Education;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Education\EducationTiming;
 use App\Entity\Education\EducationTiming;
@@ -18,12 +19,12 @@ final class CurrentEducationTimingExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (EducationTiming::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === EducationTiming::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentEducationTimingExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Network/CurrentNetworkOrganizationExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Network;
 namespace App\Doctrine\Network;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Network\NetworkOrganization;
 use App\Entity\Network\NetworkOrganization;
@@ -18,12 +19,12 @@ final class CurrentNetworkOrganizationExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (NetworkOrganization::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === NetworkOrganization::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentNetworkOrganizationExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Organization/CurrentOrganizationAddressPostalExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Organization;
 namespace App\Doctrine\Organization;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Organization\OrganizationAddressPostal;
 use App\Entity\Organization\OrganizationAddressPostal;
@@ -18,12 +19,12 @@ final class CurrentOrganizationAddressPostalExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (OrganizationAddressPostal::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === OrganizationAddressPostal::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentOrganizationAddressPostalExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 7 - 6
src/Doctrine/Organization/CurrentOrganizationArticleExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Organization;
 namespace App\Doctrine\Organization;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Organization\OrganizationArticle;
 use App\Entity\Organization\OrganizationArticle;
@@ -18,12 +19,12 @@ final class CurrentOrganizationArticleExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (OrganizationArticle::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === OrganizationArticle::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];
@@ -32,4 +33,4 @@ final class CurrentOrganizationArticleExtension extends AbstractExtension
             ->setParameter('organization', $currentUser->getOrganization())
             ->setParameter('organization', $currentUser->getOrganization())
         ;
         ;
     }
     }
-}
+}

+ 6 - 5
src/Doctrine/Organization/CurrentOrganizationExtension.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 
 namespace App\Doctrine\Organization;
 namespace App\Doctrine\Organization;
 
 
+use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;
 use App\Entity\Organization\Organization;
@@ -18,12 +19,12 @@ final class CurrentOrganizationExtension extends AbstractExtension
     public function __construct(private Security $security)
     public function __construct(private Security $security)
     { }
     { }
 
 
-    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
-    {
-        if (Organization::class !== $resourceClass) {
-            return;
-        }
+    public function supports(string $resourceClass, ?Operation $operation): bool {
+        return $resourceClass === Organization::class;
+    }
 
 
+    protected function addWhere(QueryBuilder $queryBuilder, string $resourceClass, ?Operation $operation): void
+    {
         /** @var Access $currentUser */
         /** @var Access $currentUser */
         $currentUser = $this->security->getUser();
         $currentUser = $this->security->getUser();
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $rootAlias = $queryBuilder->getRootAliases()[0];

+ 6 - 1
src/Entity/Access/Access.php

@@ -65,8 +65,13 @@ use Symfony\Component\Serializer\Annotation\Groups;
 
 
 /**
 /**
  * Fais le lien entre une Person et une Organization
  * Fais le lien entre une Person et une Organization
+ *
+ * Security :
+ *
+ *     @see ~/config/api_platform/Access/access.yaml
+ *     @see App\Doctrine\Access\CurrentAccessExtension
  */
  */
-#[ApiResource] // Config in config/api_platform/Access/access.yaml
+#[ApiResource]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: AccessRepository::class)]
 #[ORM\Entity(repositoryClass: AccessRepository::class)]
 #[ApiFilter(filterClass: BooleanFilter::class, properties: ['person.isPhysical'])]
 #[ApiFilter(filterClass: BooleanFilter::class, properties: ['person.isPhysical'])]

+ 2 - 8
src/Entity/Access/AccessFamily.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class AccessFamily
 class AccessFamily
@@ -102,4 +96,4 @@ class AccessFamily
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 1
src/Entity/Access/OrganizationFunction.php

@@ -15,7 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Fonction d'un Access dans une Organization sur une période donnée
  * Fonction d'un Access dans une Organization sur une période donnée
  */
  */
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: OrganizationFunctionRepository::class)]
 #[ORM\Entity(repositoryClass: OrganizationFunctionRepository::class)]
 #[DateTimeConstraintAware(startDateFieldName: "startDate", endDateFieldName: "endDate")]
 #[DateTimeConstraintAware(startDateFieldName: "startDate", endDateFieldName: "endDate")]

+ 6 - 1
src/Entity/Access/PersonalizedList.php

@@ -14,6 +14,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
 
 
 /**
 /**
  * Liste personnalisées
  * Liste personnalisées
+ *
+ * Security :
+ *
+ *     @see \App\Doctrine\Access\CurrentUserPersonalizedListExtension
+ *
  */
  */
 #[ApiResource(
 #[ApiResource(
     operations: [
     operations: [
@@ -25,7 +30,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
         )
         )
     ],
     ],
     paginationEnabled: false
     paginationEnabled: false
-)]
+)] // @see App\Doctrine\Access\CurrentUserPersonalizedListExtension
 #[ORM\Entity(repositoryClass: PersonalizedListRepository::class)]
 #[ORM\Entity(repositoryClass: PersonalizedListRepository::class)]
 class PersonalizedList
 class PersonalizedList
 {
 {

+ 2 - 8
src/Entity/Billing/AccessBilling.php

@@ -14,13 +14,7 @@ use Doctrine\Common\Collections\Collection;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class AccessBilling
 class AccessBilling
@@ -179,4 +173,4 @@ class AccessBilling
         $this->access = $access;
         $this->access = $access;
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 1
src/Entity/Billing/AccessIntangible.php

@@ -14,7 +14,7 @@ use Doctrine\Common\Collections\Collection;
 /**
 /**
  * Enregistrement d'un produit à facturer par un Access
  * Enregistrement d'un produit à facturer par un Access
  */
  */
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: AccessIntangibleRepository::class)]
 #[ORM\Entity(repositoryClass: AccessIntangibleRepository::class)]
 class AccessIntangible extends AbstractBillingIntangible
 class AccessIntangible extends AbstractBillingIntangible

+ 1 - 7
src/Entity/Billing/AccessPayer.php

@@ -17,13 +17,7 @@ use Doctrine\ORM\Mapping as ORM;
  * Fais le lien entre l'Access qui règle la facture et l'Access concerné
  * Fais le lien entre l'Access qui règle la facture et l'Access concerné
  *
  *
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccessPayer().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Table(name: 'BillingPayer')]
 #[ORM\Table(name: 'BillingPayer')]
 #[ORM\Entity(repositoryClass: AccessPayerRepository::class)]
 #[ORM\Entity(repositoryClass: AccessPayerRepository::class)]

+ 2 - 8
src/Entity/Billing/Bill.php

@@ -13,13 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Bill, et supprimer l'attribut discr.
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Bill, et supprimer l'attribut discr.
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'BillAccounting')]
 #[ORM\Table(name: 'BillAccounting')]
@@ -42,4 +36,4 @@ class Bill extends BillAccounting implements BillAccountingInterface
         $this->access = $access;
         $this->access = $access;
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Billing/BillAccounting.php

@@ -16,13 +16,7 @@ use Doctrine\Common\Collections\Collection;
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table BillAccounting, et supprimer l'attribut discr.
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table BillAccounting, et supprimer l'attribut discr.
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class BillAccounting
 class BillAccounting

+ 2 - 8
src/Entity/Billing/BillCredit.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
  *
  *
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'BillAccounting')]
 #[ORM\Table(name: 'BillAccounting')]
@@ -43,4 +37,4 @@ class BillCredit extends BillAccounting implements BillAccountingInterface
         $this->access = $access;
         $this->access = $access;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Billing/BillLine.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class BillLine
 class BillLine
@@ -91,4 +85,4 @@ class BillLine
         $this->equipmentLoan = $equipmentLoan;
         $this->equipmentLoan = $equipmentLoan;
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Billing/BillingIntangibleExcludeDate.php

@@ -12,13 +12,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class BillingIntangibleExcludeDate
 class BillingIntangibleExcludeDate

+ 1 - 5
src/Entity/Billing/BillingSetting.php

@@ -13,11 +13,7 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\Mapping as ORM;
 use JetBrains\PhpStorm\Pure;
 use JetBrains\PhpStorm\Pure;
 
 
-#[ApiResource(
-    operations: [
-        new Get()
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: BillingSettingRepository::class)]
 #[ORM\Entity(repositoryClass: BillingSettingRepository::class)]
 class BillingSetting
 class BillingSetting

+ 2 - 8
src/Entity/Billing/EducationalProjectPayer.php

@@ -15,13 +15,7 @@ use Doctrine\ORM\Mapping as ORM;
  *
  *
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getEducationalProjectPayer().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Table(name: 'BillingPayer')]
 #[ORM\Table(name: 'BillingPayer')]
 #[ORM\Entity]
 #[ORM\Entity]
@@ -67,4 +61,4 @@ class EducationalProjectPayer
         $this->educationalProjectReceiver = $educationalProjectReceiver;
         $this->educationalProjectReceiver = $educationalProjectReceiver;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Billing/FamilyQuotient.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class FamilyQuotient
 class FamilyQuotient
@@ -117,4 +111,4 @@ class FamilyQuotient
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 5 - 18
src/Entity/Billing/ResidenceArea.php

@@ -19,25 +19,12 @@ use Doctrine\Common\Collections\Collection;
 
 
 /**
 /**
  * Zone de résidence d'un Access, telle que définie par l'Organization
  * Zone de résidence d'un Access, telle que définie par l'Organization
+ *
+ * Security :
+ *
+ *     @see \App\Doctrine\Billing\CurrentResidenceAreaExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Delete(
-            security: 'object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        ),
-        new Post()
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
-)]
+#[ApiResource(operations: [])] // @see App\Doctrine\Billing\CurrentResidenceAreaExtension
 //#[Auditable]
 //#[Auditable]
 #[BillingSettingDefaultValue(fieldName: "billingSetting")]
 #[BillingSettingDefaultValue(fieldName: "billingSetting")]
 #[ORM\Entity(repositoryClass: ResidenceAreaRepository::class)]
 #[ORM\Entity(repositoryClass: ResidenceAreaRepository::class)]

+ 2 - 8
src/Entity/Booking/Attendance.php

@@ -15,13 +15,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Attendance
 class Attendance
@@ -114,4 +108,4 @@ class Attendance
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Booking/AttendanceBooking.php

@@ -12,13 +12,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class AttendanceBooking
 class AttendanceBooking

+ 5 - 9
src/Entity/Booking/Course.php

@@ -25,15 +25,11 @@ use Doctrine\Common\Collections\Collection;
  * @todo : migration table tag_booking
  * @todo : migration table tag_booking
  *
  *
  * Classe Course qui permet de gérer les cours de la structure.
  * Classe Course qui permet de gérer les cours de la structure.
+ *
+ * Security :
+ *   * @see App\Doctrine\Booking\CurrentCoursesExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_COURSE_VIEW\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection()
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: CourseRepository::class)]
 #[ORM\Entity(repositoryClass: CourseRepository::class)]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]
@@ -401,4 +397,4 @@ class Course extends AbstractBooking
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Booking/EducationalProject.php

@@ -24,13 +24,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @todo : migration table tag_booking
  * @todo : migration table tag_booking
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Entity]
 #[ORM\Entity]
@@ -461,4 +455,4 @@ class EducationalProject extends AbstractBooking
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Booking/Event.php

@@ -23,13 +23,7 @@ use Symfony\Component\Validator\Constraints as Assert;
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Event, et supprimer l'attribut discr.
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Event, et supprimer l'attribut discr.
  * @todo : migration table tag_booking
  * @todo : migration table tag_booking
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]

+ 2 - 8
src/Entity/Booking/EventUser.php

@@ -12,13 +12,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class EventUser
 class EventUser
@@ -60,4 +54,4 @@ class EventUser
         $this->guest = $guest;
         $this->guest = $guest;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Booking/Examen.php

@@ -22,13 +22,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @todo : migration table tag_booking
  * @todo : migration table tag_booking
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]
@@ -324,4 +318,4 @@ class Examen extends AbstractBooking
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Booking/OrganizationHoliday.php

@@ -16,13 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
  *
  *
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]
@@ -92,4 +86,4 @@ class OrganizationHoliday extends AbstractBooking
         $this->organization = $organization;
         $this->organization = $organization;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Booking/PersonHoliday.php

@@ -16,13 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
  *
  *
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Booking')]
 #[ORM\Table(name: 'Booking')]
@@ -91,4 +85,4 @@ class PersonHoliday extends AbstractBooking
         $this->access = $access;
         $this->access = $access;
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 1
src/Entity/Core/AbstractInformation.php

@@ -26,7 +26,7 @@ use Symfony\Component\Serializer\Annotation\Context;
  *
  *
  * Classe Notification. qui permet de gérer les notifications aux utilisateurs.
  * Classe Notification. qui permet de gérer les notifications aux utilisateurs.
  */
  */
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Information')]
 #[ORM\Table(name: 'Information')]

+ 7 - 8
src/Entity/Core/AddressPostal.php

@@ -17,14 +17,13 @@ use Doctrine\ORM\Mapping as ORM;
 use App\Entity\Person\PersonAddressPostal;
 use App\Entity\Person\PersonAddressPostal;
 use Symfony\Component\Serializer\Annotation\Groups;
 use Symfony\Component\Serializer\Annotation\Groups;
 
 
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getOrganizationAddressPostal().getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection()
-    ]
-)]
+/**
+ * Adresse postale d'une organisation ou d'une personne
+ *
+ * Security :
+ *   * @see App\Doctrine\Core\AllowedAddressPostalExtension
+ */
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: AddressPostalRepository::class)]
 #[ORM\Entity(repositoryClass: AddressPostalRepository::class)]
 class AddressPostal
 class AddressPostal

+ 4 - 15
src/Entity/Core/BankAccount.php

@@ -21,22 +21,11 @@ use Symfony\Component\Validator\Constraints as Assert;
 
 
 /**
 /**
  * Données bancaire d'une Person ou d'une Organization
  * Données bancaire d'une Person ou d'une Organization
+ *
+ * Security :
+ *   * @see App\Security\Voter\BankAccountVoter
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted("BANK_ACCOUNT_READ", object)'
-        ),
-        new Put(
-            security: 'is_granted("BANK_ACCOUNT_EDIT", object)'
-        ),
-        new Delete(
-            security: 'is_granted("BANK_ACCOUNT_DELETE", object)'
-        ),
-        new Post(),
-        new GetCollection()
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: BankAccountRepository::class)]
 #[ORM\Entity(repositoryClass: BankAccountRepository::class)]
 class BankAccount
 class BankAccount

+ 4 - 15
src/Entity/Core/ContactPoint.php

@@ -25,22 +25,11 @@ use App\Validator\Core as OpentalentAssert;
 
 
 /**
 /**
  * Données de contact d'une Person ou d'une Organization ou d'un lieu
  * Données de contact d'une Person ou d'une Organization ou d'un lieu
+ *
+ * Security :
+ *   * @see App\Security\Voter\ContactPointVoter
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted("CONTACT_POINT_READ", object)'
-        ),
-        new Put(
-            security: 'is_granted("CONTACT_POINT_EDIT", object)'
-        ),
-        new Delete(
-            security: 'is_granted("CONTACT_POINT_DELETE", object)'
-        ),
-        new Post(),
-        new GetCollection()
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: ContactPointRepository::class)]
 #[ORM\Entity(repositoryClass: ContactPointRepository::class)]
 #[OpentalentAssert\ContactPoint]
 #[OpentalentAssert\ContactPoint]

+ 46 - 6
src/Entity/Core/File.php

@@ -5,6 +5,8 @@ namespace App\Entity\Core;
 
 
 use ApiPlatform\Metadata\Put;
 use ApiPlatform\Metadata\Put;
 use ApiPlatform\Metadata\Get;
 use ApiPlatform\Metadata\Get;
+use ApiPlatform\Metadata\Post;
+use ApiPlatform\Metadata\Delete;
 use ApiPlatform\Metadata\ApiResource;
 use ApiPlatform\Metadata\ApiResource;
 use App\Entity\AccessWish\DocumentWish;
 use App\Entity\AccessWish\DocumentWish;
 use App\Entity\Booking\Event;
 use App\Entity\Booking\Event;
@@ -28,10 +30,18 @@ use Symfony\Component\Serializer\Annotation\Groups;
 use Symfony\Component\Validator\Constraints as Assert;
 use Symfony\Component\Validator\Constraints as Assert;
 use App\Enum\Core\FileStatusEnum;
 use App\Enum\Core\FileStatusEnum;
 
 
+/**
+ * Fichier, généré ou uploadé, appartenant à une organisation ou à une personne.
+ *
+ * Security :
+ *   * @see App\Security\Voter\FileVoter
+ */
 #[ApiResource(
 #[ApiResource(
     operations: [
     operations: [
-        new Get(),
-        new Put()
+        new Get(security: "is_granted('READ', object)"),
+        new Put(security: "is_granted('EDIT', object)"),
+        new Post(security: "is_granted('CREATE', object)"),
+        new Delete(security: "is_granted('DELETE', object)")
     ]
     ]
 )]
 )]
 //#[Auditable]
 //#[Auditable]
@@ -172,10 +182,14 @@ class File
 
 
     //    #[ORM\Column]
     //    #[ORM\Column]
     //    private ?int $eventReport_id;
     //    private ?int $eventReport_id;
-    //
-    //    #[ORM\Column]
-    //    private ?\DateTime $availabilityDate;
-    //
+
+    /**
+     * TODO: complete
+     * @var DateTime|null
+     */
+    #[ORM\Column]
+    private ?\DateTime $availabilityDate;
+
     //    #[ORM\Column]
     //    #[ORM\Column]
     //    private ?int $documentWish_id;
     //    private ?int $documentWish_id;
     //
     //
@@ -187,6 +201,7 @@ class File
     //
     //
     //    #[ORM\Column]
     //    #[ORM\Column]
     //    private ?int $work_id;
     //    private ?int $work_id;
+
     #[ORM\ManyToMany(targetEntity: Person::class, inversedBy: 'personFiles')]
     #[ORM\ManyToMany(targetEntity: Person::class, inversedBy: 'personFiles')]
     #[Groups(['file_person'])]
     #[Groups(['file_person'])]
     private Collection $accessPersons;
     private Collection $accessPersons;
@@ -367,6 +382,15 @@ class File
         return $this;
         return $this;
     }
     }
 
 
+    /**
+     * A priori inutilisé
+     * @deprecated
+     * @return Collection
+     */
+    public function getAccessRoles(): Collection {
+        return new ArrayCollection([]);
+    }
+
     /**
     /**
      * @return string
      * @return string
      */
      */
@@ -604,6 +628,22 @@ class File
         return $this;
         return $this;
     }
     }
 
 
+    /**
+     * @return DateTime|null
+     */
+    public function getAvailabilityDate(): ?DateTime
+    {
+        return $this->availabilityDate;
+    }
+
+    /**
+     * @param DateTime|null $availabilityDate
+     */
+    public function setAvailabilityDate(?DateTime $availabilityDate): void
+    {
+        $this->availabilityDate = $availabilityDate;
+    }
+
     /**
     /**
      * @return Collection<int, Person>
      * @return Collection<int, Person>
      */
      */

+ 4 - 1
src/Entity/Core/Notification.php

@@ -24,7 +24,10 @@ use Symfony\Component\Serializer\Annotation\Context;
 /**
 /**
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Notification, et supprimer l'attribut discr.
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table Notification, et supprimer l'attribut discr.
  *
  *
- * Classe Notification. qui permet de gérer les notifications aux utilisateurs.
+ * Notification à un utilisateur
+ *
+ * Security :
+ *   * @see App\Doctrine\Core\CurrentUserNotificationExtension
  */
  */
 #[ApiResource(
 #[ApiResource(
     operations: [
     operations: [

+ 3 - 0
src/Entity/Core/NotificationUser.php

@@ -16,6 +16,9 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Les NotificationUser permettent de garder la trace des notifications et des tips
  * Les NotificationUser permettent de garder la trace des notifications et des tips
  * qui ont été lues par les utilisateurs
  * qui ont été lues par les utilisateurs
+ *
+ * Security :
+ *   * @see App\Doctrine\Core\CurrentUserNotificationUserExtension
  */
  */
 #[ApiResource(
 #[ApiResource(
     operations: [
     operations: [

+ 2 - 8
src/Entity/Core/Tagg.php

@@ -41,13 +41,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Tagg
 class Tagg
@@ -863,4 +857,4 @@ class Tagg
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 10
src/Entity/Core/Tips.php

@@ -14,16 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
 //#[Auditable]
 //#[Auditable]
-#[ApiResource(
-    operations: [
-        new Get(),
-        new GetCollection(
-            paginationMaximumItemsPerPage: 20,
-            paginationClientItemsPerPage: true,
-            order: ['id' => 'DESC']
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Tips extends AbstractInformation
 class Tips extends AbstractInformation

+ 1 - 7
src/Entity/Donor/Donor.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Partenaire / Sponsor de la structure; les logo des donors apparaissent sur le site web de la structure
  * Partenaire / Sponsor de la structure; les logo des donors apparaissent sur le site web de la structure
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Donor
 class Donor

+ 2 - 8
src/Entity/Education/CriteriaNotation.php

@@ -16,13 +16,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class CriteriaNotation
 class CriteriaNotation
@@ -182,4 +176,4 @@ class CriteriaNotation
         $this->noteMax = $noteMax;
         $this->noteMax = $noteMax;
         return $this;
         return $this;
     }
     }
-}
+}

+ 4 - 14
src/Entity/Education/Cycle.php

@@ -19,21 +19,11 @@ use Doctrine\Common\Collections\Collection;
 /**
 /**
  * Enum des cycles éducatifs, utilisés par les EducationCurriculum
  * Enum des cycles éducatifs, utilisés par les EducationCurriculum
  * NB: le nombre de cycles est fixé à 6, mais chaque Organization peut en modifier le label
  * NB: le nombre de cycles est fixé à 6, mais chaque Organization peut en modifier le label
+ *
+ * Security :
+ *   * @see App\Doctrine\Education\CurrentCycleExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        )
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: CycleRepository::class)]
 #[ORM\Entity(repositoryClass: CycleRepository::class)]
 class Cycle
 class Cycle

+ 2 - 8
src/Entity/Education/Education.php

@@ -17,13 +17,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getEducationCategory().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Education
 class Education
@@ -275,4 +269,4 @@ class Education
         $this->educationNotationConfig = $educationNotationConfig;
         $this->educationNotationConfig = $educationNotationConfig;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Education/EducationCategory.php

@@ -16,13 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class EducationCategory
 class EducationCategory
@@ -87,4 +81,4 @@ class EducationCategory
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Education/EducationCurriculum.php

@@ -15,13 +15,7 @@ use Doctrine\Common\Collections\Collection;
 /**
 /**
  * Curriculum éducatif; composé d'un cycle, d'une année et d'un niveau
  * Curriculum éducatif; composé d'un cycle, d'une année et d'un niveau
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getEducation().getEducationCategory().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: EducationCurriculumRepository::class)]
 #[ORM\Entity(repositoryClass: EducationCurriculumRepository::class)]
 class EducationCurriculum
 class EducationCurriculum

+ 2 - 8
src/Entity/Education/EducationNotation.php

@@ -15,13 +15,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getEducationStudent().getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: EducationNotationRepository::class)]
 #[ORM\Entity(repositoryClass: EducationNotationRepository::class)]
 class EducationNotation
 class EducationNotation
@@ -119,4 +113,4 @@ class EducationNotation
         }
         }
         return round($this->note, 2);
         return round($this->note, 2);
     }
     }
-}
+}

+ 5 - 14
src/Entity/Education/EducationNotationConfig.php

@@ -21,21 +21,12 @@ use Symfony\Component\Validator\Constraints as Assert;
 
 
 /**
 /**
  * Configuration des grilles d'évaluation
  * Configuration des grilles d'évaluation
+ *
+ * Security :
+ *   * @see App\Doctrine\Education\CurrentEducationNotationConfigExtension
+ *
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_PEDAGOGICS_ADMINISTRATION_VIEW\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_PEDAGOGICS_ADMINISTRATION_VIEW\')'
-        )
-    ],
-    security: 'is_granted(\'ROLE_PEDAGOGICS_ADMINISTRATION\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: EducationNotationConfigRepository::class)]
 #[ORM\Entity(repositoryClass: EducationNotationConfigRepository::class)]
 #[OrganizationDefaultValue(fieldName: "organization")]
 #[OrganizationDefaultValue(fieldName: "organization")]

+ 2 - 8
src/Entity/Education/EducationStudent.php

@@ -16,13 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class EducationStudent
 class EducationStudent
@@ -220,4 +214,4 @@ class EducationStudent
         $this->educationTiming = $educationTiming;
         $this->educationTiming = $educationTiming;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Education/EducationTeacher.php

@@ -15,13 +15,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class EducationTeacher
 class EducationTeacher
@@ -100,4 +94,4 @@ class EducationTeacher
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 5 - 18
src/Entity/Education/EducationTiming.php

@@ -20,25 +20,12 @@ use Doctrine\Common\Collections\Collection;
 
 
 /**
 /**
  * Temps d'un enseignement
  * Temps d'un enseignement
+ *
+ * Security:
+ *
+ *    @see \App\Doctrine\Education\CurrentEducationTimingExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Delete(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        ),
-        new Post()
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[OrganizationDefaultValue(fieldName: "organization")]
 #[OrganizationDefaultValue(fieldName: "organization")]
 #[ORM\Entity(repositoryClass: EducationTimingRepository::class)]
 #[ORM\Entity(repositoryClass: EducationTimingRepository::class)]

+ 1 - 7
src/Entity/Network/Network.php

@@ -18,13 +18,7 @@ use Doctrine\Common\Collections\Collection;
 /**
 /**
  * Enum des différents réseaux auxquels peut appartenir une Organization
  * Enum des différents réseaux auxquels peut appartenir une Organization
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(),
-        new GetCollection()
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: NetworkRepository::class)]
 #[ORM\Entity(repositoryClass: NetworkRepository::class)]
 class Network
 class Network

+ 4 - 13
src/Entity/Network/NetworkOrganization.php

@@ -16,20 +16,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
 
 
 /**
 /**
  * Fait le lien entre une Organization et un Network
  * Fait le lien entre une Organization et un Network
+ *
+ * Security :
+ *   * @see App\Doctrine\Network\CurrentNetworkOrganizationExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted("ROLE_ORGANIZATION_VIEW" and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        )
-    ],
-    normalizationContext: ['groups' => ['network']
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: NetworkOrganizationRepository::class)]
 #[ORM\Entity(repositoryClass: NetworkOrganizationRepository::class)]
 #[DateTimeConstraintAware(startDateFieldName: "startDate", endDateFieldName: "endDate")]
 #[DateTimeConstraintAware(startDateFieldName: "startDate", endDateFieldName: "endDate")]

+ 2 - 8
src/Entity/Organization/Activity.php

@@ -18,13 +18,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Activity
 class Activity
@@ -188,4 +182,4 @@ class Activity
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Organization/Jury.php

@@ -17,13 +17,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Jury
 class Jury
@@ -144,4 +138,4 @@ class Jury
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 6 - 13
src/Entity/Organization/Organization.php

@@ -48,20 +48,13 @@ use Symfony\Component\Validator\Constraints as Assert;
 
 
 /**
 /**
  * Structure, organisation
  * Structure, organisation
+ *
+ * Security :
+ *   * @see App\Doctrine\Organization\CurrentOrganizationExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: '(is_granted("ROLE_ORGANIZATION_VIEW") or is_granted("ROLE_ORGANIZATION")) and object.getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'is_granted("ROLE_ORGANIZATION") and object.getId() == user.getOrganization().getId()'
-        ),
-        new Post(),
-        new GetCollection()
-    ],
-    processor: OrganizationProcessor::class
-)]
+#[ApiResource(operations: [
+    new Get(security: "object.getId() == user.getOrganization().getId()")
+])] //
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: OrganizationRepository::class)]
 #[ORM\Entity(repositoryClass: OrganizationRepository::class)]
 class Organization
 class Organization

+ 7 - 16
src/Entity/Organization/OrganizationAddressPostal.php

@@ -19,25 +19,16 @@ use Symfony\Component\Validator\Constraints as Assert;
 use Symfony\Component\Serializer\Annotation\Groups;
 use Symfony\Component\Serializer\Annotation\Groups;
 use App\Validator\Organization as OpentalentAssert;
 use App\Validator\Organization as OpentalentAssert;
 
 
+/**
+ * Fait le lien entre une adresse postal et une organisation
+ *
+ * Security :
+ *   * @see App\Doctrine\Organization\CurrentOrganizationAddressPostalExtension
+ */
 #[ApiResource(
 #[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Delete(
-            security: 'object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        ),
-        new Post()
-    ],
     normalizationContext: ['groups' => ['address']],
     normalizationContext: ['groups' => ['address']],
     denormalizationContext: ['groups' => ['address']],
     denormalizationContext: ['groups' => ['address']],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
+    operations: []
 )]
 )]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: OrganizationAddressPostalRepository::class)]
 #[ORM\Entity(repositoryClass: OrganizationAddressPostalRepository::class)]

+ 5 - 12
src/Entity/Organization/OrganizationArticle.php

@@ -12,19 +12,12 @@ use App\Repository\Organization\OrganizationArticleRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\Mapping as ORM;
 
 
 /**
 /**
- * Fait le lien entre une Organization et un coup de projecteur
+ * Fait le lien entre une Organization et un article (ou coup de projecteur, tel qu'affiché dans l'annuaire des structures)
+ *
+ * Security :
+ *   *  @see App\Doctrine\Organization\CurrentOrganizationArticleExtension
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted("ROLE_ORGANIZATION_VIEW" and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(
-            security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\')'
-        )
-    ],
-    security: 'is_granted(\'ROLE_ORGANIZATION\')'
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: OrganizationArticleRepository::class)]
 #[ORM\Entity(repositoryClass: OrganizationArticleRepository::class)]
 class OrganizationArticle
 class OrganizationArticle

+ 1 - 1
src/Entity/Organization/OrganizationLicence.php

@@ -16,7 +16,7 @@ use App\Entity\Access\Access;
 use App\Repository\Organization\OrganizationLicenceRepository;
 use App\Repository\Organization\OrganizationLicenceRepository;
 //use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 //use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\Mapping as ORM;
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: OrganizationLicenceRepository::class)]
 #[ORM\Entity(repositoryClass: OrganizationLicenceRepository::class)]
 class OrganizationLicence
 class OrganizationLicence

+ 1 - 1
src/Entity/Organization/Settings.php

@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Caractéristiques d'une Organization (produits, options...etc)
  * Caractéristiques d'une Organization (produits, options...etc)
  */
  */
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: SettingsRepository::class)]
 #[ORM\Entity(repositoryClass: SettingsRepository::class)]
 class Settings
 class Settings

+ 8 - 13
src/Entity/Organization/Subdomain.php

@@ -3,6 +3,7 @@ declare (strict_types=1);
 
 
 namespace App\Entity\Organization;
 namespace App\Entity\Organization;
 
 
+use ApiPlatform\Action\NotFoundAction;
 use ApiPlatform\Metadata\Post;
 use ApiPlatform\Metadata\Post;
 use ApiPlatform\Metadata\GetCollection;
 use ApiPlatform\Metadata\GetCollection;
 use ApiPlatform\Metadata\Put;
 use ApiPlatform\Metadata\Put;
@@ -23,19 +24,13 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Sous-domaine enregistré par une organisation
  * Sous-domaine enregistré par une organisation
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: '(is_granted("ROLE_ORGANIZATION_VIEW") or is_granted("ROLE_ORGANIZATION")) and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new Put(
-            security: 'is_granted("ROLE_ORGANIZATION") and object.getOrganization().getId() == user.getOrganization().getId()'
-        ),
-        new GetCollection(),
-        new Post()
-    ],
-    processor: SubdomainProcessor::class
-)]
+#[ApiResource(operations: [
+    new Get(
+        controller: NotFoundAction::class,
+        read: false,
+        output: false
+    ),
+])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: SubdomainRepository::class)]
 #[ORM\Entity(repositoryClass: SubdomainRepository::class)]
 #[OrganizationDefaultValue(fieldName: "organization")]
 #[OrganizationDefaultValue(fieldName: "organization")]

+ 2 - 10
src/Entity/Organization/TypeOfPractice.php

@@ -19,15 +19,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Type des pratique d'une organisation
  * Type des pratique d'une organisation
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(),
-        new GetCollection(
-            normalizationContext: ['groups' => ['read']]
-        )
-    ],
-    paginationEnabled: false
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: TypeOfPracticeRepository::class)]
 #[ORM\Entity(repositoryClass: TypeOfPracticeRepository::class)]
 class TypeOfPractice
 class TypeOfPractice
@@ -108,4 +100,4 @@ class TypeOfPractice
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Person/Commission.php

@@ -16,13 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Commission
 class Commission
@@ -116,4 +110,4 @@ class Commission
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Person/CommissionMember.php

@@ -13,13 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class CommissionMember
 class CommissionMember

+ 2 - 8
src/Entity/Person/CompanyPerson.php

@@ -13,13 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getAccess().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class CompanyPerson
 class CompanyPerson
@@ -65,4 +59,4 @@ class CompanyPerson
         $this->access = $access;
         $this->access = $access;
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Person/Person.php

@@ -26,13 +26,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
 /**
 /**
  * Personne physique ou morale
  * Personne physique ou morale
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_USERS_VIEW\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: PersonRepository::class)]
 #[ORM\Entity(repositoryClass: PersonRepository::class)]
 class Person implements UserInterface, PasswordAuthenticatedUserInterface
 class Person implements UserInterface, PasswordAuthenticatedUserInterface

+ 1 - 1
src/Entity/Person/PersonActivity.php

@@ -22,7 +22,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Lien entre une Person et une Activity
  * Lien entre une Person et une Activity
  */
  */
-#[ApiResource]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: PersonActivityRepository::class)]
 #[ORM\Entity(repositoryClass: PersonActivityRepository::class)]
 class PersonActivity
 class PersonActivity

+ 1 - 7
src/Entity/Person/PersonAddressPostal.php

@@ -16,13 +16,7 @@ use Symfony\Component\Validator\Constraints as Assert;
 /**
 /**
  * Lien entre une Person et une AddressPostal
  * Lien entre une Person et une AddressPostal
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_USERS_VIEW\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity(repositoryClass: PersonAddressPostalRepository::class)]
 #[ORM\Entity(repositoryClass: PersonAddressPostalRepository::class)]
 class PersonAddressPostal
 class PersonAddressPostal

+ 1 - 7
src/Entity/Place/Place.php

@@ -25,13 +25,7 @@ use Doctrine\ORM\Mapping as ORM;
  *
  *
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Place')]
 #[ORM\Table(name: 'Place')]

+ 2 - 8
src/Entity/Place/Room.php

@@ -20,13 +20,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getPlace().getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class Room
 class Room
@@ -370,4 +364,4 @@ class Room
         }
         }
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Product/Equipment.php

@@ -21,13 +21,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @todo : migration table tag_product
  * @todo : migration table tag_product
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Product')]
 #[ORM\Table(name: 'Product')]
@@ -398,4 +392,4 @@ class Equipment extends AbstractProduct
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 1 - 7
src/Entity/Product/EquipmentLoan.php

@@ -18,13 +18,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class EquipmentLoan
 class EquipmentLoan

+ 2 - 8
src/Entity/Product/Intangible.php

@@ -20,13 +20,7 @@ use App\Entity\Education\EducationCurriculum;
  * @todo : migration table tag_product
  * @todo : migration table tag_product
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\') and object.getOrganization().getId() == user.getOrganization().getId()'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 #[ORM\Table(name: 'Product')]
 #[ORM\Table(name: 'Product')]
@@ -150,4 +144,4 @@ class Intangible extends AbstractProduct
         $this->tags->removeElement($tag);
         $this->tags->removeElement($tag);
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 8
src/Entity/Product/IntangibleDiscountDetail.php

@@ -14,13 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
 /**
 /**
  * Classe ... qui ...
  * Classe ... qui ...
  */
  */
-#[ApiResource(
-    operations: [
-        new Get(
-            security: 'is_granted(\'ROLE_ADMIN\')'
-        )
-    ]
-)]
+#[ApiResource(operations: [])]
 //#[Auditable]
 //#[Auditable]
 #[ORM\Entity]
 #[ORM\Entity]
 class IntangibleDiscountDetail
 class IntangibleDiscountDetail
@@ -79,4 +73,4 @@ class IntangibleDiscountDetail
         $this->familyQuotient = $familyQuotient;
         $this->familyQuotient = $familyQuotient;
         return $this;
         return $this;
     }
     }
-}
+}

+ 2 - 0
src/Enum/Core/FileTypeEnum.php

@@ -8,10 +8,12 @@ use MyCLabs\Enum\Enum;
  * @method static UNKNOWN()
  * @method static UNKNOWN()
  * @method static NONE()
  * @method static NONE()
  * @method static LICENCE_CMF()
  * @method static LICENCE_CMF()
+ * @method static BILL()
  */
  */
 class FileTypeEnum extends Enum
 class FileTypeEnum extends Enum
 {
 {
     private const UNKNOWN = 'UNKNOWN';
     private const UNKNOWN = 'UNKNOWN';
     private const NONE = 'NONE';
     private const NONE = 'NONE';
     private const LICENCE_CMF ='LICENCE_CMF';
     private const LICENCE_CMF ='LICENCE_CMF';
+    private const BILL ='BILL';
 }
 }

+ 19 - 0
src/Enum/Core/FileVisibilityEnum.php

@@ -0,0 +1,19 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Enum\Core;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Statuts des fichiers
+ * @method static EVERYBODY()
+ * @method static NOBODY()
+ * @method static ONLY_ORGANIZATION()
+ */
+class FileVisibilityEnum extends Enum
+{
+    private const EVERYBODY = 'EVERYBODY';
+    private const NOBODY = 'NOBODY';
+    private const ONLY_ORGANIZATION = 'ONLY_ORGANIZATION';
+}

+ 187 - 0
src/Security/Voter/AbstractVoter.php

@@ -0,0 +1,187 @@
+<?php
+
+namespace App\Security\Voter;
+
+use App\Entity\Access\Access;
+use App\Service\Access\Utils;
+use App\Service\Security\InternalRequestsService;
+use App\Service\Security\SwitchUser;
+use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Bundle\SecurityBundle\Security;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\Voter;
+use Symfony\Component\Security\Core\User\UserInterface;
+
+/**
+ * Base class for custom Voters.
+ *
+ * This class also defines a default behavior for entity based voters (ex: FileVoter)
+ *
+ * @see doc/security.md
+ */
+abstract class AbstractVoter extends Voter
+{
+    protected const READ = 'READ';
+    protected const EDIT = 'EDIT';
+    protected const CREATE = 'CREATE';
+    protected const DELETE = 'DELETE';
+
+    /**
+     * The current user if any, else null; access it trough isUserLoggedIn or getUser methods
+     * @var Access|null
+     */
+    private ?Access $user = null;
+
+    /**
+     * The supported class name. Override it in subclass.
+     * Ex:
+     *     protected ?string $entityClass = File::class;
+     *
+     * @var string|null
+     */
+    protected static ?string $entityClass = null;
+
+    /**
+     * List of supported operations. Override it to restrict.
+     * @var array<string>
+     */
+    protected static array $allowedOperations = [
+        self::READ, self::EDIT, self::CREATE, self::DELETE
+    ];
+
+    public function __construct(
+        private Security $security,
+        protected Utils $accessUtils,
+        private InternalRequestsService $internalRequestsService,
+        EntityManagerInterface $em,
+        private SwitchUser $switchUser
+    ) {
+        /** @var Access $user */
+        $user = $this->security->getUser();
+
+        // <-- Special case of impersonated users: the switch user is not setup yet by symfony, we have to do it "manually"
+        $switchHeaderId = $_SERVER['HTTP_X_SWITCH_USER'] ?? null;
+        if ($switchHeaderId !== null) {
+            $switchAs = $em->find(Access::class, $switchHeaderId);
+            if (
+                $switchAs &&
+                (
+                    $this->security->isGranted('ROLE_ALLOWED_TO_SWITCH') ||
+                    $this->switchUser->isAllowedToSwitch($user, $switchAs)
+                )
+            ) {
+                $user = $switchAs;
+            }
+        }
+        // -->
+
+        // If the user is not anonymous, remember it
+        if ($user instanceof Access) {
+            $this->user = $user;
+        }
+    }
+
+    /**
+     * Default `supports` method, that uses self::entityClass and self::allowedOperations to determine if the voter
+     * supports the subject and attribute.
+     *
+     * @param string $attribute
+     * @param mixed $subject
+     * @return bool
+     */
+    protected function supports(string $attribute, mixed $subject): bool
+    {
+        if (static::$entityClass === null) {
+            throw new \RuntimeException('Setup the self::$entityClass property, or override the supports() method');
+        }
+
+        return $subject::class === static::$entityClass && in_array($attribute, static::$allowedOperations);
+    }
+
+    /**
+     * Default `voteOnAttribute` method, calling one of the `canXxxx()` method, according to the attribute.
+     *
+     * @param string $attribute
+     * @param mixed $subject
+     * @param TokenInterface $token
+     * @return bool
+     */
+    protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
+    {
+        switch ($attribute) {
+            case self::READ:
+                return $this->canView($subject);
+            case self::EDIT:
+                return $this->canEdit($subject);
+            case self::CREATE:
+                return $this->canCreate($subject);
+            case self::DELETE:
+                return $this->canDelete($subject);
+        }
+
+        return false;
+    }
+
+    /**
+     * Does the client have the right to view this resource?
+     * @param object $subject
+     * @return bool
+     */
+    abstract protected function canView(object $subject): bool;
+
+    /**
+     * Does the client have the right to edit this resource?
+     * @param object $subject
+     * @return bool
+     */
+    abstract protected function canEdit(object $subject): bool;
+
+    /**
+     * Does the client have the right to create this resource?
+     *
+     * @param object $subject
+     * @return bool
+     */
+    abstract protected function canCreate(object $subject): bool;
+
+    /**
+     * Does the client have the right to delete this resource?
+     *
+     * @param object $subject
+     * @return bool
+     */
+    abstract protected function canDelete(object $subject): bool;
+
+    /**
+     * Returns the current logged in user
+     * @return Access
+     */
+    protected function getUser(): ?Access {
+        return $this->user;
+    }
+
+    /**
+     * Is the client an authenticated user ?
+     *
+     * @return bool
+     */
+    protected function isUserLoggedIn(): bool {
+        return $this->getUser() !== null;
+    }
+
+    /**
+     * Is the current request a valid internal request?
+     *
+     * @see doc/internal_requests.md
+     * @see doc/security.md
+     *
+     * @return bool
+     */
+    protected function isValidInternalRequest(): bool {
+        $clientIp = $_SERVER['REMOTE_ADDR'] ?? null;
+        $internalRequestToken = $_SERVER['HTTP_INTERNAL_REQUESTS_TOKEN'] ?? '';
+
+        return $this->internalRequestsService->isAllowed($clientIp, $internalRequestToken);
+    }
+
+}

+ 208 - 0
src/Security/Voter/Core/FileVoter.php

@@ -0,0 +1,208 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Security\Voter\Core;
+
+use App\Entity\Access\Access;
+use App\Entity\Core\File;
+use App\Enum\Core\FileVisibilityEnum;
+use App\Enum\Core\FileTypeEnum;
+use App\Security\Voter\AbstractVoter;
+use App\Service\Utils\DatesUtils;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+
+/**
+ * Contrôle l'accès à l'entité File
+ */
+class FileVoter extends AbstractVoter
+{
+    /**
+     * @inheritdoc
+     * @var string|null
+     */
+    protected static ?string $entityClass = File::class;
+
+    /**
+     * @inheritdoc
+     *
+     * @param $subject File
+     * @return boolean
+     */
+    protected function canView(object $subject): bool
+    {
+        // Le fichier n'est pas encore disponible
+        if(!$this->isAvailable($subject, $this->getUser())) {
+            // TODO: à revoir
+            return false;
+        }
+
+        // File has public visibility
+        if ($subject->getVisibility() === FileVisibilityEnum::EVERYBODY()->getValue()) {
+            return true;
+        }
+
+        // Is this an internal request?
+        if ($this->isValidInternalRequest()) {
+            return true;
+        }
+
+        // If the user is not logged in, the file is not available
+        if ($this->isUserLoggedIn()) {
+
+            // If the logged user is in the accessPersons of the File
+            if (
+                $subject->getAccessPersons()->contains($this->getUser()->getPerson())
+            ) {
+                return true;
+            }
+
+            // User is the owner of the file
+            if ($subject->getPerson() && $subject->getPerson()->getId() === $this->getUser()->getPerson()->getId()) {
+                return true;
+            }
+
+            // User has ROLE_FILE and owns the file or belongs to the organization who does
+            if (
+                $this->accessUtils->hasRole($this->getUser(), 'ROLE_FILE') &&
+                $this->isInFileOwningGroup($subject, $this->getUser())
+            ) {
+                return true;
+            }
+
+            // User has the requested role and owns the file or belongs to the organization who does (deprecated)
+            if (
+                $this->isInFileOwningGroupWithRole($subject, $this->getUser())
+            ) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @inheritdoc
+     *
+     * @param $subject File
+     * @return boolean
+     */
+    protected function canEdit(object $subject): bool
+    {
+        // Le fichier n'est pas encore disponible
+        if(!$this->isAvailable($subject, $this->getUser())) {
+            return false;
+        }
+
+        if ($this->isUserLoggedIn()) {
+
+            // User has ROLE_FILE and has access to the file
+            if (
+                $this->accessUtils->hasRole($this->getUser(), 'ROLE_FILE') &&
+                $this->isInFileOwningGroup($subject, $this->getUser())
+            ) {
+                return true;
+            }
+
+            // User is the owner of the file
+            if ($subject->getPerson() && $subject->getPerson()->getId() === $this->getUser()->getPerson()->getId()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @inheritdoc
+     *
+     * @param $subject File
+     * @return boolean
+     */
+    protected function canCreate(object $subject): bool
+    {
+        // An authenticated user can create a file
+        if ($this->isUserLoggedIn()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @inheritdoc
+     *
+     * @param $subject File
+     * @return boolean
+     */
+    protected function canDelete(object $subject): bool {
+        return $this->canEdit($subject);
+    }
+
+    /**
+     * Is the given user the owner of the file, or does he belong to the organization that owns it, and does the user
+     * have any of the required role to access this file?
+     *
+     * @param File $file
+     * @param Access $user
+     * @return bool
+     */
+    protected function isInFileOwningGroup(File $file, Access $user): bool {
+        $isOrganizationOwned = $file->getOrganization() && $file->getOrganization()->getId() === $user->getOrganization()->getId();
+        $isAccessOwned = $file->getPerson() && $file->getPerson()->getId() === $user->getPerson()->getId();
+
+        return $isOrganizationOwned || $isAccessOwned;
+    }
+
+    /**
+     * Is the given user the owner of the file, or does he belong to the organization that owns it, and does the user
+     * have any of the required role to access this file?
+     *
+     * @deprecated
+     * @param File $file
+     * @param Access $user
+     * @return bool
+     */
+    protected function isInFileOwningGroupWithRole(File $file, Access $user): bool {
+
+        $requiredRoles = $file->getAccessRoles();
+        if ($requiredRoles->count() === 0) {
+            return false;
+        }
+
+        $userHasRequiredRole = $file->getAccessRoles()->exists(
+            // TODO: implémenter une méthode hasAnyRoleAmong() côté accessUtils
+            fn($aR) => $this->accessUtils->hasRole($user, $aR)
+        );
+
+        return $this->isInFileOwningGroup($file, $user) && $userHasRequiredRole;
+    }
+
+    /**
+     * Is the file available now?
+     * Depends on the availabilityDate, the type of the file and the user
+     *
+     * @param $file File
+     * @return bool
+     * @throws \Exception
+     */
+    protected function isAvailable(File $file, ?Access $user): bool {
+
+        $today = DatesUtils::new();
+
+        if ($file->getAvailabilityDate() !== null && $file->getAvailabilityDate() > $today) {
+
+            //  Cas particulier de la liste des factures
+            // TODO: clarifier le ou les cas particulier(s) et les sortir de cette méthode (celle ci devrait tenir en deux lignes max)
+            if (
+                $user &&
+                $file->getType() === FileTypeEnum::BILL()->getValue() &&
+                $this->accessUtils->hasRole($user, 'ROLE_BILLACCOUNTING')
+            ) {
+                return true;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+}

+ 2 - 1
src/Security/Voter/SwitchUserVoter.php

@@ -42,10 +42,11 @@ class SwitchUserVoter extends Voter
             return true;
             return true;
         }
         }
 
 
+        // TODO: on pourrait pas intégrer le isGranted à cette méthode?
         if ($this->switchUser->isAllowedToSwitch($user, $subject)) {
         if ($this->switchUser->isAllowedToSwitch($user, $subject)) {
             return true;
             return true;
         }
         }
 
 
         return true;
         return true;
     }
     }
-}
+}

Some files were not shown because too many files changed in this diff