From 115d3785caaa72e256b82e07915e5dec3347b9d6 Mon Sep 17 00:00:00 2001 From: Gabriel Matte Date: Tue, 19 Nov 2024 17:01:26 -0500 Subject: [PATCH] Adds Auth Docs + Starting quizroom + stylise --- documentation/dev.py | 2 + .../docs/developpeur/backend/auth.md | 288 ++++++++++++++++++ .../docs/developpeur/backend/salle-de-quiz.md | 146 +++++++++ .../docs/developpeur/deploiements/prod.md | 2 +- .../developpeur/documentation/a-propos.md | 1 + documentation/docs/utilisateur/deploiment.md | 13 + documentation/mkdocs.yml | 36 ++- documentation/requirements.txt | 3 +- 8 files changed, 487 insertions(+), 4 deletions(-) create mode 100644 documentation/dev.py create mode 100644 documentation/docs/developpeur/backend/auth.md create mode 100644 documentation/docs/developpeur/backend/salle-de-quiz.md create mode 100644 documentation/docs/utilisateur/deploiment.md diff --git a/documentation/dev.py b/documentation/dev.py new file mode 100644 index 0000000..12e9687 --- /dev/null +++ b/documentation/dev.py @@ -0,0 +1,2 @@ +from plantuml import PlantUML +server = PlantUML(url='http://localhost:8080/plantuml') diff --git a/documentation/docs/developpeur/backend/auth.md b/documentation/docs/developpeur/backend/auth.md new file mode 100644 index 0000000..9400e8f --- /dev/null +++ b/documentation/docs/developpeur/backend/auth.md @@ -0,0 +1,288 @@ +# Authentification + +Le but du module d'authentification est de pouvoir facilement faire des blocks de code permettant une authentification personalisée. Il est possible de le faire grâce a cette architecture. + +```plantuml +@startuml + +package Backend { +class AuthManager{ + +IAuthModule[] auths + #userInfos + + -load() + -registerAuths() + +showAuths() + + +authStatus() + +logIn(UserInfos) + +register(UserInfos) + +logOut() +} + +interface IAuthModule{ + +registerAuth() + +authenticate() + +register() + +showAuth() +} + +class SimpleFormAuthModule{ + +} + +class PassportAuthModule{ + IPassportProviderDefinition[] providers +} + +Interface IPassportProviderDefinition{ + +name + +type +} + +class OAuthPassportProvider{ + +clientId + +clientSecret + +configUrl + +authorizeUrl + +tokenUrl + +userinfoUrl + +logoutUrl + +JWKSUrl +} + +IAuthModule <|-- SimpleFormAuthModule +IAuthModule <|-- PassportAuthModule +IPassportProviderDefinition <|-- OAuthPassportProvider + +AuthManager -> IAuthModule +PassportAuthModule -> IPassportProviderDefinition +} + +package Frontend{ + class AuthDrawer{ + +IAuthVisual[] getAuthsVisual() + +drawAuths() + } + + Interface IAuthVisual{ + +draw() + } + + class FormVisual{ + +FormInput[] formInputs + } + + interface FormInput{ + +name + +label + +type + +value + } + + AuthDrawer -> IAuthVisual + IAuthVisual <|-- FormVisual + FormVisual -> FormInput +} + +@enduml +``` + + +Le fonctionnement peut être expliqué avec les diagrammes suivants : + + +## Module : Passport Js +```plantuml +@startuml + +box "Frontend" +participant User +Participant App +end box + +box "Backend" +participant PassportAuthModule +participant Db +participant AuthManager +end box + +box "Auth Server" +participant AuthServer +end box + +User -> App : Get auth page +App -> User : auth page + +User -> App : click OAuth button +App -> User : redirect to OAuth + +User -> AuthServer: Login +AuthServer -> User: Redirect to Auth endpoint with token + +User -> PassportAuthModule: Authenticate with token + +PassportAuthModule -> AuthServer: get user info +AuthServer -> PassportAuthModule: userInfo + +alt login + PassportAuthModule -> Db : fetch local userInfo + Db->PassportAuthModule: userInfo + PassportAuthModule -> PassportAuthModule: Merge userInfo definition + PassportAuthModule -> Db : update user profile + Db->PassportAuthModule: userInfo +end + +alt register + PassportAuthModule -> Db : fetch local userInfo + Db->PassportAuthModule: null + PassportAuthModule -> Db : create user profile + Db->PassportAuthModule: userInfo +end + +PassportAuthModule -> AuthManager : login(userInfos) + +AuthManager -> User: Give refresh token + Redirect to page +User -> App: get / +App -> User: Show Authenticated / +@enduml +``` + +## Module : SimpleAuth +```plantuml +@startuml + +box "Frontend" +participant User +Participant App +end box + +box "Backend" +participant SimpleAuthModule +participant Db +participant AuthManager +end box + +User -> App : Get auth page +App -> User : auth page + + +alt Login + User -> App : Send Login/Pass + + App -> SimpleAuthModule: Send login/pass + + SimpleAuthModule -> Db: get user info + Db->SimpleAuthModule: user info + SimpleAuthModule -> SimpleAuthModule: Validate Hash +end + +alt register + User -> App : Send Username + Password + Email + + App -> SimpleAuthModule: Send Username + Password + Email + + SimpleAuthModule -> Db: get user info + Db -> SimpleAuthModule : null + + SimpleAuthModule -> Db: put user info +end + +SimpleAuthModule -> AuthManager: userInfo +AuthManager -> User: Give refresh token + Redirect to page +User -> App: get / +App -> User: Show Authenticated / +@enduml +``` + +## Comment les boutons sont affichés +```plantuml +@startuml + +box "FrontEnd" +participant User +Participant FrontEnd +Participant AuthDrawer +end box + +box "BackEnd" +participant API +participant AuthManager +participant Db +participant IAuthModule +end box + +API -> API : load global configurations + +create AuthManager +API -> AuthManager : instanciate with auth configurations + + +create IAuthModule +AuthManager -> IAuthModule : instanciate array + +loop For each auth in auths + AuthManager -> IAuthModule : register + IAuthModule -> API : register routes + API -> IAuthModule : route registration confirmation + IAuthModule -> AuthManager : module registration confirmation +end + +User -> FrontEnd : get login page + +alt already logged in + FrontEnd -> User: redirected to authenticated page +end + +FrontEnd -> AuthDrawer : get auth visual +AuthDrawer -> API : get auth form data + +API -> AuthManager : get auth form data + + +loop For each auth in auths + AuthManager -> IAuthModule : get form data + IAuthModule -> AuthManager : form data +end + +AuthManager -> API : auth fom data +API -> AuthDrawer : auth form data + +AuthDrawer -> AuthDrawer : make auth html +AuthDrawer -> FrontEnd : auth HTML +FrontEnd -> User : show auth page + + +@enduml +``` + +## Comment les sessions sont conservées +```plantuml +@startuml + box "Frontend" + participant User + Participant App + end box + + box "Backend" + participant AuthManager + participant IAuthModules + end box + + App -> AuthManager : send refresh token + + AuthManager -> IAuthModules: ForEach check if logged + IAuthModules -> AuthManager: is authenticated ? + + alt one logged in + AuthManager -> App : send new token + end + + alt all logged out + AuthManager -> App : send error + App -> App : destroy token + App -> User : redirect to login page + end + +@enduml +``` \ No newline at end of file diff --git a/documentation/docs/developpeur/backend/salle-de-quiz.md b/documentation/docs/developpeur/backend/salle-de-quiz.md new file mode 100644 index 0000000..a45e0ff --- /dev/null +++ b/documentation/docs/developpeur/backend/salle-de-quiz.md @@ -0,0 +1,146 @@ +# Salles de Quiz + +Les salles de quiz ont été extraites dans leur propre container afin de limiter les dégats liés soit a une surutilisation d'une salle ou une attaque sur le logiciel. + +En éffet, le découplement permet a un quiz de: + + - Survivre même si le backend est non-fonctionnel + - Mourir sans entrainer toute l'application avec elle + - Créer/Supprimer des salles automatiquement dépendant de la demande + + Pour éffectuer ceci il faut éffectuer une petite gymnastique. Il y a une route dans l'api servant à gerer les salles. Lorsqu'un utilisateur demande le socket d'une salle : "/api/rooms/{id}/socket", la requette rebondit sur le proxy Nginx. Celui-ci contacte le backend afin d'obtenir l'addresse de l'ordinateur auquel envoyer la requette et redirige le socket vers ce pc. + + +## Diagramme de séquence +```plantuml +@startuml + actor Teacher + actor Student + entity Nginx + entity Frontend + entity Api + entity Docker + entity Database + +group Quiz Creation + Teacher -> Frontend : Create a quizroom + Frontend -> Api : Create a quizroom + Api -> Docker : Create a quizroom + Docker -> QuizRoom ** + QuizRoom -> Docker : creation successful + Docker -> Api : Creation Successful + + loop every seconds until healthy or 30s: + Api -> QuizRoom : Checking Health via /health + QuizRoom -> Api : Doesn't answer, answer healthy or unhealthy + end + + Api -> Database : Create Room + Database -> Api : Room created + Api -> Teacher : Route to room socket +end + +group Quiz Joining: + Teacher -> Nginx : Join Room + Nginx -> Api : Get room infos from id + Api -> Nginx : Ip:port of room + Nginx -> QuizRoom: Give teacher's connexion + + Student -> Frontend: Join Room X + Frontend -> Nginx : Join Room X + Nginx -> Api : Get room infos from id + Api -> Nginx : Ip:port of room + Nginx -> QuizRoom: Give student's connexion + + QuizRoom -> QuizRoom : Give Quiz ... (Multiple actions) + + Student -> QuizRoom: Disconnect + Teacher -> QuizRoom: Disconect +end + +group QuizManagement (Every 10 seconds) + Api -> QuizRoom : Checking number of people in the room + QuizRoom -> Api : Number of people (0) or Unhealthy + Api -> Database : Mark room to deletion +end + +group Quiz Deletion (Every 30 seconds) + Api -> Database : Give all rooms marked for deletion + Database -> Api : rooms + Api -> Docker : delete rooms + Docker -> QuizRoom : delete + Docker -> Api : Deleted +end + +@enduml +``` + +## Classes touchant cette fonctionalitée +```plantuml +@startuml +class Room{ + +id + +name + +host + +nbStudents + +mustBeCleaned +} + +class RoomRepository { + +get(id) + +create(room) + +delete(id) + +update(room,id) + +getAll() +} + +class RoomController { + +setupRoom(options) + +deleteRoom(roomId) + +listRooms() + +getRoomStatus(roomId) + +updateRoom(room,roomId) +} + +class RoomRouter{ + + / : GET + + /:id : GET + + / : POST + + /:id : PUT + + /:id : DELETE +} + +class BaseRoomProvider { + +createRoom(roomid,options) + +deleteRoom(roomId) + +getRoomInfo(roomId) + +getRoomStatus(roomId) + +listRooms() + -cleanup() + -syncInstantiatedRooms() + #updateRoomInfos() +} + +class DockerRoomProvider +circle Dockerode + + +Room - RoomRepository +BaseRoomProvider o-- RoomRepository +DockerRoomProvider --|> BaseRoomProvider +DockerRoomProvider - Dockerode +Dockerode o-- QuizRoom +RoomController o-- BaseRoomProvider +RoomRouter o-- RoomController + +class QuizRoom{ + +create-room() + +join-room() + +next-question() + +launch-student-mode() + +end-quiz() + +submit-answers() + -disconnect() +} +@enduml +``` \ No newline at end of file diff --git a/documentation/docs/developpeur/deploiements/prod.md b/documentation/docs/developpeur/deploiements/prod.md index a7ad466..127d083 100644 --- a/documentation/docs/developpeur/deploiements/prod.md +++ b/documentation/docs/developpeur/deploiements/prod.md @@ -5,7 +5,7 @@ Nous avons choisi d'exécuter les composantes de cette application avec Docker, Voici un diagramme de déploiement expliquant la relation des composantes et comment les images Docker sont créées et déployées dans un serveur. -```puml +```plantuml @startuml [Navigateur moderne (Windows/Android)] as Navigateur [MongoDB] as MongoDB diff --git a/documentation/docs/developpeur/documentation/a-propos.md b/documentation/docs/developpeur/documentation/a-propos.md index c97bf3e..2af1023 100644 --- a/documentation/docs/developpeur/documentation/a-propos.md +++ b/documentation/docs/developpeur/documentation/a-propos.md @@ -4,6 +4,7 @@ Pour lancer la documentation, il faut installer python et entrer dans le dossier documentation. Il faut ensuite installer les dépendances avec `pip install -r requirements.txt`. Pour lancer le mode développement il faut executer `python -m mkdocs serve` +Afin d'accellerer le déploiement et ne pas être touché par des érreurs de "rate-limiting", il est préférable d'utiliser une image docker de plantuml. Pour cela, il faut utiliser la commande suivante : `docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat` ## Deploiement Le code est automatiquement déployé par la github-action `create-docs.yaml` diff --git a/documentation/docs/utilisateur/deploiment.md b/documentation/docs/utilisateur/deploiment.md new file mode 100644 index 0000000..34e0806 --- /dev/null +++ b/documentation/docs/utilisateur/deploiment.md @@ -0,0 +1,13 @@ +# Déploiement + +Les méthodes recommandées de déploiement sont via Ansible et Opentofu. +Ansible est utilisés afin de faire un déploiement sur un serveur local, opentofu sur le cloud. + +## Ansible + +Le déploiement avec ansible est un déploiement simplifié. +Il vous suffis d'avoir un ordinateur linux/mac ou pouvant faire exécuter [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) dans le cas de windows. Il faut ensuite utiliser le gestionnaire de paquet (souvent apt) afin d'installer le paquet `ansible-core`, d'autres méthodes sont indiquées dans la [documentation officielle de ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html). Une fois le tout fais, vous pouvez telécharger [les fichiers nécéssaire](https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir/ansible) et lancer la commande `ansible-playbook -i inventory.ini deploy.yml` + +## OpenTofu +Le déploiement avec OpenTofu est un peu plus complexe mais il permet d'héberger la solution sur votre cloud préféré. +Il suffit [d'installer OpenTofu](https://opentofu.org/docs/intro/install/) et de téléchgarger [les fichiers nécéssaires](https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir/opentofu). Un Readme est inclus afin d'organiser votre grape de serveurs. \ No newline at end of file diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index 1302bf7..8e37b83 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -1,12 +1,38 @@ site_name: EvalueTonSavoir +repo_url: https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir +edit_uri: edit/main/documentation/docs theme: + language: fr + icon: + repo: fontawesome/brands/github name: material + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: deep purple + accent: purple + toggle: + icon: material/brightness-7 + name: Mode sombre + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: deep purple + accent: purple + toggle: + icon: material/brightness-4 + name: Mode clair features: - content.code.copy - content.code.select - content.code.annotate - locale: fr + - navigation.instant + - navigation.instant.progress + - navigation.tracking + - content.action.edit highlightjs: true hljs_languages: - javascript @@ -22,8 +48,14 @@ use_directory_urls: false plugins: - search + - offline - plantuml: - puml_url: https://www.plantuml.com/plantuml/ + puml_url: http://localhost:8080/plantuml # dev + puml_url: https://www.plantuml.com/plantuml/ # default + puml_keyword: plantuml + theme: + light: material/deep-purple-light + dark: material/deep-purple-dark markdown_extensions: - pymdownx.highlight: diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 1feb057..4dab312 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -3,4 +3,5 @@ mkdocs[i18n] mkdocs_puml mkdocs-material Pygments -ghp-import \ No newline at end of file +ghp-import +plantuml \ No newline at end of file