diff --git a/main.py b/main.py index 530c87f..3ee4965 100644 --- a/main.py +++ b/main.py @@ -1,13 +1,13 @@ """Script implementing the MVC pattern using SQLAlchemy ORM and WTForms To activate Sentry : -1. Create a new Sentry project at https://sentry.io/ and get your dsn_key -2. in the .env file, replace 'DEBUG=True' by 'SENTRY=dsn_key' +1. Create a new Sentry project at https://sentry.io/ and get your 'dsn_key' +2. in the .env file, set SENTRY_DSN='dsn_key' without any quotes """ - from src.controllers.login import Controller +from src.utils.shell import clear_shell if __name__ == "__main__": - Controller.clear_shell() + clear_shell() Controller.run() diff --git a/setup.cfg b/setup.cfg index ab3d7e4..8149385 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,5 +8,5 @@ htmldir = flake8_html_report python_files = test* [coverage:run] -omit = tests/*, main.py, src/settings/* +omit = tests/*, main.py, src/settings/*, shell.py, fernet.py diff --git a/src/controllers/collaborator.py b/src/controllers/collaborator.py index 176d088..112ce1c 100644 --- a/src/controllers/collaborator.py +++ b/src/controllers/collaborator.py @@ -3,6 +3,7 @@ from ..models.collaborator import Collaborator from ..models.department import Department from ..utils.fernet import Fernet +from ..utils.session import session_is_expired from ..views.collaborator import View model1 = Collaborator @@ -14,8 +15,17 @@ class Controller: def run(self, session, user): self._menu(session, user) + @classmethod + def return_to_menu(self, session, user): + self._menu(session, user) + @classmethod def _menu(self, session, user) -> bool: + if session_is_expired(user): + View.logout() + + return None + View.print_menu() choice = View.get_user_choice() @@ -43,7 +53,7 @@ def _menu(self, session, user) -> bool: if choice == 6: self._delete(session) - self._menu(session, user) + self.return_to_menu(session, user) @classmethod def _list(self, session): diff --git a/src/controllers/contract.py b/src/controllers/contract.py index c3133b9..effd13c 100644 --- a/src/controllers/contract.py +++ b/src/controllers/contract.py @@ -2,6 +2,7 @@ from ..models.contract_event import Contract from ..models.customer import Customer +from ..utils.session import session_is_expired from ..utils.slugify import slugify from ..views.contract import View @@ -14,8 +15,17 @@ class Controller: def run(self, session, user): self._menu(session, user) + @classmethod + def return_to_menu(self, session, user): + self._menu(session, user) + @classmethod def _menu(self, session, user) -> bool: + if session_is_expired(user): + View.logout() + + return None + if user.role == "Gestion": View.print_gestion_menu() @@ -56,7 +66,7 @@ def _menu(self, session, user) -> bool: if choice == 8 and user.role == "Gestion": self._delete(session) - self._menu(session, user) + self.return_to_menu(session, user) @classmethod def _list(self, session): diff --git a/src/controllers/customer.py b/src/controllers/customer.py index 6475b8f..a907ec9 100644 --- a/src/controllers/customer.py +++ b/src/controllers/customer.py @@ -1,6 +1,7 @@ # pylint: disable=no-member from ..models.company import Company from ..models.customer import Customer +from ..utils.session import session_is_expired from ..utils.slugify import slugify from ..views.customer import View @@ -10,8 +11,17 @@ class Controller: def run(self, session, user): self._menu(session, user) + @classmethod + def return_to_menu(self, session, user): + self._menu(session, user) + @classmethod def _menu(self, session, user) -> bool: + if session_is_expired(user): + View.logout() + + return None + if user.role == "Commercial": View.print_commercial_menu() else: @@ -42,7 +52,7 @@ def _menu(self, session, user) -> bool: if choice == 6 and user.role == "Commercial": self._delete(session, user) - self._menu(session, user) + self.return_to_menu(session, user) @classmethod def _list(self, session): diff --git a/src/controllers/event.py b/src/controllers/event.py index d8faa2b..75cb442 100644 --- a/src/controllers/event.py +++ b/src/controllers/event.py @@ -2,6 +2,7 @@ from ..models.collaborator import Collaborator from ..models.contract_event import Contract, Event from ..models.location import Location +from ..utils.session import session_is_expired from ..utils.slugify import slugify from ..views.event import View @@ -14,8 +15,17 @@ class Controller: def run(self, session, user): self._menu(session, user) + @classmethod + def return_to_menu(self, session, user): + self._menu(session, user) + @classmethod def _menu(self, session, user) -> bool: + if session_is_expired(user): + View.logout() + + return None + if user.role == "Gestion": View.print_gestion_menu() @@ -59,7 +69,7 @@ def _menu(self, session, user) -> bool: if choice == 6 and user.role == "Commercial": self._delete(session, user) - self._menu(session, user) + self.return_to_menu(session, user) @classmethod def _list(self, session): diff --git a/src/controllers/home.py b/src/controllers/home.py index a89e855..58e914f 100644 --- a/src/controllers/home.py +++ b/src/controllers/home.py @@ -1,3 +1,4 @@ +from ..utils.session import session_is_expired from ..views.home import View from . import collaborator, contract, customer, event @@ -7,8 +8,17 @@ class Controller: def run(self, session, user): self._menu(session, user) + @classmethod + def return_to_menu(self, session, user): + self._menu(session, user) + @classmethod def _menu(self, session, user): + if session_is_expired(user): + View.logout() + + return None + if user.role == "Gestion": View.print_gestion_menu() else: @@ -19,6 +29,8 @@ def _menu(self, session, user): if choice == 0: View.logout() + return None + if choice == 1: customer.Controller.run(session, user) @@ -31,4 +43,4 @@ def _menu(self, session, user): if choice == 4 and user.role == "Gestion": collaborator.Controller.run(session, user) - self._menu(session, user) + self.return_to_menu(session, user) diff --git a/src/controllers/login.py b/src/controllers/login.py index a75bb3d..d44ab0a 100644 --- a/src/controllers/login.py +++ b/src/controllers/login.py @@ -1,5 +1,3 @@ -import os - from ..forms.collaborator import FirstConnexionForm from ..models.collaborator import Collaborator from ..models.department import Department @@ -10,18 +8,31 @@ class Controller: + session = init_db() + @classmethod def run(self): - session = init_db() + # session = init_db() + + # collaborators = session.query(Collaborator).all() - collaborators = session.query(Collaborator).all() + # if collaborators: + # self._menu(session) + # else: + # self._create_gestion_account(session) + # self._menu(session) + + collaborators = self.session.query(Collaborator).all() if collaborators: - self._menu(session) + self._menu(self.session) else: - self._create_gestion_account(session) + self._create_gestion_account(self.session) + self._menu(self.session) - self.run() + @classmethod + def return_to_menu(self, session): + self._menu(session) @classmethod def _menu(self, session): @@ -33,6 +44,8 @@ def _menu(self, session): # 1. quit View.logout() + return None + if choice == 1: # 1. login user = self._login(session) @@ -48,7 +61,7 @@ def _menu(self, session): user = self._first_login(session) self._create_password(session, user) - self._menu(session) + self.return_to_menu(session) @classmethod def _create_gestion_account(self, session): @@ -172,10 +185,3 @@ def _create_password(self, session, user): View.print_password_update_success() else: View.print_forms_errors(form) - - @classmethod - def clear_shell(self): - if os.name == "posix": # Unix/Linux - os.system("clear") - if os.name == "nt": # Windows - os.system("cls") diff --git a/src/models/collaborator.py b/src/models/collaborator.py index b0d28f9..4a33c97 100644 --- a/src/models/collaborator.py +++ b/src/models/collaborator.py @@ -1,5 +1,7 @@ +from datetime import datetime + import bcrypt -from sqlalchemy import Column, Date, DateTime, ForeignKey, Integer, String, func +from sqlalchemy import Column, Date, DateTime, ForeignKey, Integer, String from sqlalchemy.orm import relationship from src.utils.fernet import Fernet @@ -87,7 +89,7 @@ def check_password(self, password: str, hashed_password: bytes) -> bool: def authenticate(self, session, email: str, password: str): collaborator = self.get_with_clear_email(session, email) if collaborator and self.check_password(password, collaborator.password): - collaborator.last_login = func.now() + collaborator.last_login = datetime.utcnow() session.commit() return collaborator diff --git a/src/settings/settings.py b/src/settings/settings.py index 6d859a5..5ce1ef6 100644 --- a/src/settings/settings.py +++ b/src/settings/settings.py @@ -31,8 +31,6 @@ def create_fernet() -> Fernet: fernet = create_fernet() -PUBLIC_KEY = "public_key" - def init_db(Base=Base): engine = create_engine("sqlite:///" + str(BASE_DIR / "db.sqlite3")) diff --git a/src/utils/jwt_token.py b/src/utils/jwt_token.py deleted file mode 100644 index fb6400a..0000000 --- a/src/utils/jwt_token.py +++ /dev/null @@ -1,37 +0,0 @@ -from datetime import datetime, timedelta - -import jwt - -from ..models.collaborator import Collaborator -from ..settings.settings import ENCRYPTION_KEY, PUBLIC_KEY - - -class JwtToken: - @staticmethod - def generate(collaborator, session) -> str: - payload = { - "id": collaborator.id, - "role": collaborator.role(session), - "exp": datetime.utcnow() + timedelta(days=1), - } - jwt_token = jwt.encode(payload, ENCRYPTION_KEY, algorithm="HS256") - - return jwt_token - - @staticmethod - def check(jwt_token, session) -> Collaborator: - """check if the jwt token is valid: - return the collaborator instance if the token is valid - return False if the token is invalid""" - - try: - payload = jwt.decode(jwt_token, PUBLIC_KEY, algorithms=["HS256"]) - collaborator = session.get(Collaborator, payload.get("id")) - - return collaborator - - except jwt.ExpiredSignatureError: - return False - - except jwt.InvalidTokenError: - return False diff --git a/src/utils/session.py b/src/utils/session.py new file mode 100644 index 0000000..78626fd --- /dev/null +++ b/src/utils/session.py @@ -0,0 +1,8 @@ +from datetime import datetime, timedelta + + +def session_is_expired(user) -> bool: + """used to disconnect the user after 12 hours""" + + if datetime.utcnow() > user.last_login + timedelta(hours=12): + return True diff --git a/src/utils/shell.py b/src/utils/shell.py new file mode 100644 index 0000000..1f31de7 --- /dev/null +++ b/src/utils/shell.py @@ -0,0 +1,8 @@ +import os + + +def clear_shell(): + if os.name == "posix": # Unix/Linux + os.system("clear") + if os.name == "nt": # Windows + os.system("cls") diff --git a/src/views/collaborator.py b/src/views/collaborator.py index e8eb2f7..b645c64 100644 --- a/src/views/collaborator.py +++ b/src/views/collaborator.py @@ -22,13 +22,8 @@ def print_menu(self): console.print(f"\nMenu {self.name.title()}", style="bold", justify="center") console.print("__________________", justify="center") - console.print("\n0. Retour", style="bold", justify="center") - console.print("\n1. Lister", style="bold", justify="center") - console.print("\n2. Rechercher", style="bold", justify="center") - console.print("\n3. Détail", style="bold", justify="center") - console.print("\n4. Créer", style="bold", justify="center") - console.print("\n5. Modifier", style="bold", justify="center") - console.print("\n6. Supprimer", style="bold", justify="center") + self.print_read_only_menu() + self.print_edit_menu() @classmethod def print_list(self, qs: list, list_name: str, page_size=5): diff --git a/src/views/contract.py b/src/views/contract.py index b0d2a73..9d1f6ca 100644 --- a/src/views/contract.py +++ b/src/views/contract.py @@ -37,10 +37,7 @@ def print_menu(self): console.print(f"\nMenu {self.name.title()}", style="bold", justify="center") console.print("____________", justify="center") - console.print("\n0. Retour", style="bold", justify="center") - console.print("\n1. Lister", style="bold", justify="center") - console.print("\n2. Rechercher", style="bold", justify="center") - console.print("\n3. Détail", style="bold", justify="center") + self.print_read_only_menu() @classmethod def print_list(self, qs: list[model], list_name: str, page_size=5): diff --git a/src/views/customer.py b/src/views/customer.py index 114e9f4..3c24611 100644 --- a/src/views/customer.py +++ b/src/views/customer.py @@ -19,19 +19,14 @@ class View(MixinView): @classmethod def print_commercial_menu(self): self.print_menu() - console.print("\n4. Créer", style="bold", justify="center") - console.print("\n5. Modifier", style="bold", justify="center") - console.print("\n6. Supprimer", style="bold", justify="center") + self.print_edit_menu() @classmethod def print_menu(self): console.print("\nMenu Client", style="bold", justify="center") console.print("___________", justify="center") - console.print("\n0. Retour", style="bold", justify="center") - console.print("\n1. Lister", style="bold", justify="center") - console.print("\n2. Rechercher", style="bold", justify="center") - console.print("\n3. Détail", style="bold", justify="center") + self.print_read_only_menu() @classmethod def print_list(self, qs: list, list_name: str, page_size=5): diff --git a/src/views/event.py b/src/views/event.py index ddead49..092ae7d 100644 --- a/src/views/event.py +++ b/src/views/event.py @@ -26,9 +26,7 @@ class View(MixinView): @classmethod def print_commercial_menu(self): self._print_menu() - console.print("\n4. Créer", style="bold", justify="center") - console.print("\n5. Modifier", style="bold", justify="center") - console.print("\n6. Supprimer", style="bold", justify="center") + self.print_edit_menu() @classmethod def print_gestion_menu(self): @@ -47,10 +45,7 @@ def _print_menu(self): console.print(f"\nMenu {self.name.title()}", style="bold", justify="center") console.print("______________", justify="center") - console.print("\n0. Retour", style="bold", justify="center") - console.print("\n1. Lister", style="bold", justify="center") - console.print("\n2. Rechercher", style="bold", justify="center") - console.print("\n3. Détail", style="bold", justify="center") + self.print_read_only_menu() @classmethod def print_list(self, qs: list[model], list_name: str, page_size=5): diff --git a/src/views/mixin.py b/src/views/mixin.py index 64f438f..ec05b5d 100644 --- a/src/views/mixin.py +++ b/src/views/mixin.py @@ -1,7 +1,7 @@ +import logging import sys from rich.console import Console -import logging console = Console() @@ -9,6 +9,19 @@ class MixinView: name = "" + @classmethod + def print_read_only_menu(self): + console.print("\n0. Retour", style="bold", justify="center") + console.print("\n1. Lister", style="bold", justify="center") + console.print("\n2. Rechercher", style="bold", justify="center") + console.print("\n3. Détail", style="bold", justify="center") + + @classmethod + def print_edit_menu(self): + console.print("\n4. Créer", style="bold", justify="center") + console.print("\n5. Modifier", style="bold", justify="center") + console.print("\n6. Supprimer", style="bold", justify="center") + @classmethod def print_create_success(self, obj): if obj and obj.id: diff --git a/tests/__init__.py b/tests/__init__.py index 06fca70..2cb0b9a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -32,6 +32,11 @@ def clear_db(self): def mock_prompt_ask(self, *args, **kwargs) -> str: return "user_entry" + def mock_permission_denied(self, *args, **kwargs): + """used to mock forbidden access in controllers during tests""" + + raise PermissionError("access denied") + def create_department(self, department_name: str) -> Department: department = Department(name=department_name) department.create(self.session) @@ -42,7 +47,7 @@ def create_collaborator(self, department_name: str) -> Collaborator: collaborator = Collaborator( first_name="John", last_name="Doe", - email=Fernet.encrypt("john@gmail.com"), + email=Fernet.encrypt(self.CLEAR_EMAIL), birthdate=date(2000, 1, 1), phone="0102030405", department=self.create_department(department_name), diff --git a/tests/controllers/test_collaborator.py b/tests/controllers/test_collaborator.py new file mode 100644 index 0000000..6c90311 --- /dev/null +++ b/tests/controllers/test_collaborator.py @@ -0,0 +1,109 @@ +from datetime import datetime, timedelta + +from src.controllers.collaborator import Controller +from src.views.collaborator import View +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_session_is_expired(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() - timedelta(days=1) + Controller.run(self.session, user) + + def test_run_to_home_menu(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_list(self, monkeypatch): + self.clear_db() + + # forbidden paths + + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_search(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(View, "get_searched_value", lambda *args, **kwargs: "2") + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_detail(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr(View, "get_id", lambda *args, **kwargs: "3") + monkeypatch.setattr(View, "print_detail", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) diff --git a/tests/controllers/test_contract.py b/tests/controllers/test_contract.py new file mode 100644 index 0000000..0e3ac9f --- /dev/null +++ b/tests/controllers/test_contract.py @@ -0,0 +1,109 @@ +from datetime import datetime, timedelta + +from src.controllers.contract import Controller +from src.views.contract import View +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_session_is_expired(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() - timedelta(days=1) + Controller.run(self.session, user) + + def test_run_to_home_menu(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_list(self, monkeypatch): + self.clear_db() + + # forbidden paths + + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_search(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(View, "get_searched_value", lambda *args, **kwargs: "2") + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_detail(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr(View, "get_id", lambda *args, **kwargs: "3") + monkeypatch.setattr(View, "print_detail", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) diff --git a/tests/controllers/test_customer.py b/tests/controllers/test_customer.py new file mode 100644 index 0000000..dc25101 --- /dev/null +++ b/tests/controllers/test_customer.py @@ -0,0 +1,112 @@ +from datetime import datetime, timedelta + +from src.controllers.customer import Controller +from src.views.customer import View +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_session_is_expired(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() - timedelta(days=1) + Controller.run(self.session, user) + + def test_run_to_home_menu(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + monkeypatch.setattr(View, "print_commercial_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_list(self, monkeypatch): + self.clear_db() + + # forbidden paths + + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_search(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + monkeypatch.setattr(View, "print_commercial_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(View, "get_searched_value", lambda *args, **kwargs: "2") + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Support") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_detail(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr(View, "get_id", lambda *args, **kwargs: "3") + monkeypatch.setattr(View, "print_detail", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) diff --git a/tests/controllers/test_event.py b/tests/controllers/test_event.py new file mode 100644 index 0000000..2b02a58 --- /dev/null +++ b/tests/controllers/test_event.py @@ -0,0 +1,111 @@ +from datetime import datetime, timedelta + +from src.controllers.event import Controller +from src.views.event import View +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_session_is_expired(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(View, "print_commercial_menu", self.mock_permission_denied) + monkeypatch.setattr(View, "print_support_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() - timedelta(days=1) + Controller.run(self.session, user) + + def test_run_to_home_menu(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_gestion_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_list(self, monkeypatch): + self.clear_db() + + # forbidden paths + + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_gestion_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_search(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_detail", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_gestion_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(View, "get_searched_value", lambda *args, **kwargs: "2") + monkeypatch.setattr(View, "print_list", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_detail(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(Controller, "_list", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_search", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_update", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_delete", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "print_gestion_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr(View, "get_id", lambda *args, **kwargs: "3") + monkeypatch.setattr(View, "print_detail", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) diff --git a/tests/controllers/test_home.py b/tests/controllers/test_home.py new file mode 100644 index 0000000..a69d853 --- /dev/null +++ b/tests/controllers/test_home.py @@ -0,0 +1,133 @@ +from datetime import datetime, timedelta + +from src.controllers.collaborator import Controller as CollaboratorMenu +from src.controllers.contract import Controller as ContractMenu +from src.controllers.customer import Controller as CustomerMenu +from src.controllers.event import Controller as EventMenu +from src.controllers.home import Controller +from src.views.home import View +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_session_is_expired(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(View, "get_user_choice", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() - timedelta(days=1) + Controller.run(self.session, user) + + def test_run_and_logout(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_print_menu_collaborator_as_gestion(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_menu", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_gestion_menu", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 4) + monkeypatch.setattr(CollaboratorMenu, "run", lambda *args, **kwargs: 0) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Gestion") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_print_menu_collaborator_as_commercial(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(CollaboratorMenu, "run", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 4) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: 0) + + # run + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_print_menu_customer(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(CollaboratorMenu, "run", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(CustomerMenu, "run", lambda *args, **kwargs: 0) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: 0) + + # run paths + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_print_menu_contract(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(CollaboratorMenu, "run", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(ContractMenu, "run", lambda *args, **kwargs: 0) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: 0) + + # run + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) + + def test_print_menu_event(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_gestion_menu", self.mock_permission_denied) + monkeypatch.setattr(CollaboratorMenu, "run", self.mock_permission_denied) + + # allowed paths + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr(EventMenu, "run", lambda *args, **kwargs: 0) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: 0) + + # run + user = self.create_collaborator("Commercial") + user.last_login = datetime.utcnow() + Controller.run(self.session, user) diff --git a/tests/controllers/test_login.py b/tests/controllers/test_login.py new file mode 100644 index 0000000..bff528a --- /dev/null +++ b/tests/controllers/test_login.py @@ -0,0 +1,309 @@ +from src.controllers.home import Controller as HomeMenu +from src.controllers.login import Collaborator, Controller, Fernet +from src.views.login import ( + CollaboratorForm, + FirstConnexionForm, + LoginForm, + MultiDict, + PasswordForm, + View, +) +from tests import MixinSetup + + +class TestController(MixinSetup): + def test_run_and_quit(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 0) + monkeypatch.setattr(View, "logout", lambda *args, **kwargs: None) + + # run paths + self.create_collaborator("Gestion") + Controller.run() + + def test_create_the_first_gestion_account(self, monkeypatch): + self.clear_db() + + # forbidden paths + monkeypatch.setattr(View, "print_forms_errors", self.mock_permission_denied) + monkeypatch.setattr(Controller, "return_to_menu", self.mock_permission_denied) + + # building data to mock View.get_gestion_data + input_data = { + "first_name": "John99", + "last_name": "Doe99", + "email": "john@gmail99.com", + "birthdate": "2000-01-01", + "phone": "0102030405", + } + form1 = CollaboratorForm(MultiDict(input_data)) + + # building data to mock View.create_password + input_data2 = {"password": "00000000pW-", "password_confirm": "00000000pW-"} + form2 = PasswordForm(MultiDict(input_data2)) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "get_gestion_data", lambda *args, **kwargs: form1) + monkeypatch.setattr(View, "create_password", lambda *args, **kwargs: form2) + monkeypatch.setattr( + View, "print_create_gestion_account_success", lambda *args, **kwargs: None + ) + monkeypatch.setattr(Controller, "_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + + # check if the gestion user is created + created_gestion = self.session.get(Collaborator, 1) + assert created_gestion + assert Fernet.decrypt(created_gestion.email) == input_data.get("email") + + def test_login(self, monkeypatch): + self.clear_db() + # setup user data + user = self.create_collaborator("Gestion") + user.set_password(self.CLEAR_PASSWORD) + user.save(self.session) + + assert user.last_login is None + + # building LoginForm to mock View.get_user_ids() + input_data = {"email": self.CLEAR_EMAIL, "password": self.CLEAR_PASSWORD} + form = LoginForm(MultiDict(input_data)) + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + monkeypatch.setattr(View, "logout", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_change_password", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_first_login", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create_password", self.mock_permission_denied) + monkeypatch.setattr(View, "print_login_failure", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "get_user_ids", lambda *args, **kwargs: form) + monkeypatch.setattr(View, "print_login_success", lambda *args, **kwargs: None) + monkeypatch.setattr(HomeMenu, "run", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + user = self.session.get(Collaborator, 1) + assert user + assert user.last_login + + def test_login_failure(self, monkeypatch): + self.clear_db() + + # setup data + assert len(self.session.query(Collaborator).all()) == 0 + user = self.create_collaborator("Gestion") + + assert Fernet.decrypt(user.email) != "wrong" + assert Collaborator.check_password("wrong", user.password) is False + + # building LoginForm to mock View.get_user_ids() + input_data = {"email": "wrong", "password": "wrong"} + form = LoginForm(MultiDict(input_data)) + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + monkeypatch.setattr(View, "logout", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_change_password", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_first_login", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create_password", self.mock_permission_denied) + monkeypatch.setattr(View, "print_login_success", self.mock_permission_denied) + monkeypatch.setattr(HomeMenu, "run", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 1) + monkeypatch.setattr(View, "get_user_ids", lambda *args, **kwargs: form) + monkeypatch.setattr(View, "print_login_failure", lambda *args, **kwargs: None) + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + + # check if no user were connected + all_collaborators = self.session.query(Collaborator).all() + for collaborator in all_collaborators: + assert collaborator.last_login is None + + def test_change_password(self, monkeypatch): + self.clear_db() + + # setup user data + user = self.create_collaborator("Gestion") + user.set_password(self.CLEAR_PASSWORD) + user.save(self.session) + + # building LoginForm to mock View.get_user_ids and View.change_password + input_data = {"email": self.CLEAR_EMAIL, "password": self.CLEAR_PASSWORD} + form = LoginForm(MultiDict(input_data)) + + input_data2 = { + "password": "new_Password18*", + "password_confirm": "new_Password18*", + } + form2 = PasswordForm(MultiDict(input_data2)) + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + monkeypatch.setattr(View, "logout", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_first_login", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_create_password", self.mock_permission_denied) + monkeypatch.setattr(View, "print_login_failure", self.mock_permission_denied) + monkeypatch.setattr(View, "print_forms_errors", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 2) + monkeypatch.setattr(View, "get_user_ids", lambda *args, **kwargs: form) + monkeypatch.setattr(View, "print_login_success", lambda *args, **kwargs: None) + + monkeypatch.setattr(View, "change_password", lambda *args, **kwargs: form2) + monkeypatch.setattr( + View, "print_password_update_success", lambda *args, **kwargs: None + ) + + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + + # check if the password changed + user = self.session.get(Collaborator, 1) + assert user + assert Fernet.decrypt(user.email) == self.CLEAR_EMAIL + assert Collaborator.check_password("new_Password18*", user.password) + + def test_first_connexion(self, monkeypatch): + self.clear_db() + + # setup user data + user = self.create_collaborator("Gestion") + assert user.password is None + assert user.last_login is None + + # building LoginForm to mock View.get_first_connexion_data and View.get_user_ids and View.create_password + input_data = { + "id": user.id, + "email": self.CLEAR_EMAIL, + "birthdate": user.prompt_birthdate, + } + form = FirstConnexionForm(MultiDict(input_data)) + + input_data2 = { + "password": "new_Password77*", + "password_confirm": "new_Password77*", + } + form2 = PasswordForm(MultiDict(input_data2)) + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + monkeypatch.setattr(View, "logout", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_login", self.mock_permission_denied) + monkeypatch.setattr( + Controller, "_redirect_to_home", self.mock_permission_denied + ) + monkeypatch.setattr(Controller, "_change_password", self.mock_permission_denied) + monkeypatch.setattr(View, "print_login_failure", self.mock_permission_denied) + monkeypatch.setattr(View, "print_forms_errors", self.mock_permission_denied) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr( + View, "get_first_connexion_data", lambda *args, **kwargs: form + ) + monkeypatch.setattr(View, "print_valid_forms", lambda *args, **kwargs: None) + + monkeypatch.setattr(View, "create_password", lambda *args, **kwargs: form2) + monkeypatch.setattr( + View, "print_password_update_success", lambda *args, **kwargs: None + ) + + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + + # check if the password changed + user = self.session.get(Collaborator, 1) + assert user + assert user.password is not None + assert Fernet.decrypt(user.email) == self.CLEAR_EMAIL + assert Collaborator.check_password("new_Password77*", user.password) + + def test_first_connexion_failure_with_wrong_id(self, monkeypatch): + self.clear_db() + + # setup user data + user = self.create_collaborator("Gestion") + assert user.password is None + + # building LoginForm to mock View.get_first_connexion_data and View.get_user_ids and View.create_password + input_data = { + "id": 15, + "email": self.CLEAR_EMAIL, + "birthdate": user.prompt_birthdate, + } + form = FirstConnexionForm(MultiDict(input_data)) + + # forbidden paths + monkeypatch.setattr( + Controller, "_create_gestion_account", self.mock_permission_denied + ) + monkeypatch.setattr(View, "logout", self.mock_permission_denied) + monkeypatch.setattr(Controller, "_login", self.mock_permission_denied) + monkeypatch.setattr( + Controller, "_redirect_to_home", self.mock_permission_denied + ) + monkeypatch.setattr(Controller, "_change_password", self.mock_permission_denied) + monkeypatch.setattr(View, "print_valid_forms", self.mock_permission_denied) + monkeypatch.setattr( + View, "print_password_update_success", self.mock_permission_denied + ) + + # allowed path + monkeypatch.setattr(Controller, "session", self.session) + monkeypatch.setattr(View, "print_menu", lambda *args, **kwargs: None) + monkeypatch.setattr(View, "get_user_choice", lambda *args, **kwargs: 3) + monkeypatch.setattr( + View, "get_first_connexion_data", lambda *args, **kwargs: form + ) + + monkeypatch.setattr(Controller, "return_to_menu", lambda *args, **kwargs: None) + + # run paths + Controller.run() + + # check if the password changed + user = self.session.get(Collaborator, 1) + assert user.password is None