|
|
@@ -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
|