From cecf1e69a28c486fad1aefb62040341952c04da4 Mon Sep 17 00:00:00 2001 From: berkay Date: Fri, 10 Jan 2025 20:52:45 +0300 Subject: [PATCH] mongo updated --- ApiServices/.dockerignore | 93 --- ApiServices/AuthService/app.py | 2 +- .../AuthService/application/authentication.py | 751 ++++++++++++++++++ .../middlewares/token_middleware.py | 10 +- ApiServices/AuthService/pyproject.toml | 2 +- .../routers/authentication/router.py | 2 +- ApiServices/EventService/Dockerfile | 40 +- ApiServices/EventService/app.py | 16 +- .../middlewares/token_middleware.py | 4 +- ApiServices/EventService/pyproject.toml | 2 +- ApiServices/EventService/routers/__init__.py | 102 ++- .../EventService/routers/account/router.py | 57 ++ .../routers/address/address/router.py | 54 ++ .../routers/address/post_code/router.py | 46 ++ .../EventService/routers/api/router.py | 74 ++ .../application/enums_and_drops/router.py | 98 +++ .../routers/application/occupants/router.py | 58 ++ .../routers/application/router.py} | 0 .../routers/building/buildarea/router.py | 49 ++ .../routers/building/buildparts/router.py | 50 ++ .../routers/building/buildsites/router.py | 48 ++ .../routers/building/buildtypes/router.py | 48 ++ .../routers/building/livingspaces/router.py | 53 ++ .../routers/company/company/router.py | 46 ++ .../routers/company/department/router.py | 47 ++ .../routers/company/duties/router.py | 54 ++ .../routers/company/duty/router.py | 44 + .../routers/company/employee/router.py | 59 ++ .../routers/company/staff/router.py | 50 ++ .../decision_book/decision_book/router.py | 88 ++ .../decision_book_invitations/router.py | 37 + .../decision_book_items/router.py | 61 ++ .../decision_book_person/router.py | 54 ++ .../project_decision_book/router.py | 60 ++ .../project_decision_book_items/router.py | 71 ++ .../project_decision_book_person/router.py | 71 ++ .../events/events/bind_events_router.py | 35 + .../routers/events/events/router.py | 18 + .../events/modules/bind_events_router.py | 35 + .../routers/events/modules/router.py | 47 ++ .../events/services/bind_services_router.py | 24 + .../routers/events/services/router.py | 44 + .../EventService/routers/people/router.py | 44 + .../project_decision_book/router.py | 58 ++ .../project_decision_book_person/router.py | 58 ++ .../EventService/routers/rules/router.py | 130 +++ .../EventService/routers/users/router.py | 45 ++ .../routers/validations/router.py | 123 +++ ApiServices/ValidationService/Dockerfile | 40 +- ApiServices/ValidationService/app.py | 16 +- .../middlewares/token_middleware.py | 4 +- ApiServices/ValidationService/pyproject.toml | 2 +- ApiServices/api_handlers/auth_actions/auth.py | 268 ++++--- README.md | 3 + api-docker-compose.yml | 65 +- .../events/application/authentication.py | 149 ++-- api_services/__init__.py | 10 +- api_services/redis/functions.py | 3 +- databases/extensions/auth.py | 307 +++---- databases/no_sql_models/identity.py | 34 +- databases/sql_models/identity/identity.py | 13 +- run_auth_service.sh | 4 +- service_app_init/Dockerfile | 18 +- service_app_init/runner.py | 279 ++++--- testers/authentication/test_login.py | 62 +- .../mongoLoginTesters/mongoLoginTest.py | 115 +-- 66 files changed, 3597 insertions(+), 857 deletions(-) delete mode 100644 ApiServices/.dockerignore create mode 100644 ApiServices/AuthService/application/authentication.py create mode 100644 ApiServices/EventService/routers/account/router.py create mode 100644 ApiServices/EventService/routers/address/address/router.py create mode 100644 ApiServices/EventService/routers/address/post_code/router.py create mode 100644 ApiServices/EventService/routers/api/router.py create mode 100644 ApiServices/EventService/routers/application/enums_and_drops/router.py create mode 100644 ApiServices/EventService/routers/application/occupants/router.py rename ApiServices/{__init__.py => EventService/routers/application/router.py} (100%) create mode 100644 ApiServices/EventService/routers/building/buildarea/router.py create mode 100644 ApiServices/EventService/routers/building/buildparts/router.py create mode 100644 ApiServices/EventService/routers/building/buildsites/router.py create mode 100644 ApiServices/EventService/routers/building/buildtypes/router.py create mode 100644 ApiServices/EventService/routers/building/livingspaces/router.py create mode 100644 ApiServices/EventService/routers/company/company/router.py create mode 100644 ApiServices/EventService/routers/company/department/router.py create mode 100644 ApiServices/EventService/routers/company/duties/router.py create mode 100644 ApiServices/EventService/routers/company/duty/router.py create mode 100644 ApiServices/EventService/routers/company/employee/router.py create mode 100644 ApiServices/EventService/routers/company/staff/router.py create mode 100644 ApiServices/EventService/routers/decision_book/decision_book/router.py create mode 100644 ApiServices/EventService/routers/decision_book/decision_book_invitations/router.py create mode 100644 ApiServices/EventService/routers/decision_book/decision_book_items/router.py create mode 100644 ApiServices/EventService/routers/decision_book/decision_book_person/router.py create mode 100644 ApiServices/EventService/routers/decision_book/project_decision_book/router.py create mode 100644 ApiServices/EventService/routers/decision_book/project_decision_book_items/router.py create mode 100644 ApiServices/EventService/routers/decision_book/project_decision_book_person/router.py create mode 100644 ApiServices/EventService/routers/events/events/bind_events_router.py create mode 100644 ApiServices/EventService/routers/events/events/router.py create mode 100644 ApiServices/EventService/routers/events/modules/bind_events_router.py create mode 100644 ApiServices/EventService/routers/events/modules/router.py create mode 100644 ApiServices/EventService/routers/events/services/bind_services_router.py create mode 100644 ApiServices/EventService/routers/events/services/router.py create mode 100644 ApiServices/EventService/routers/people/router.py create mode 100644 ApiServices/EventService/routers/project_decision_book/project_decision_book/router.py create mode 100644 ApiServices/EventService/routers/project_decision_book/project_decision_book_person/router.py create mode 100644 ApiServices/EventService/routers/rules/router.py create mode 100644 ApiServices/EventService/routers/users/router.py create mode 100644 ApiServices/EventService/routers/validations/router.py diff --git a/ApiServices/.dockerignore b/ApiServices/.dockerignore deleted file mode 100644 index 1449c90..0000000 --- a/ApiServices/.dockerignore +++ /dev/null @@ -1,93 +0,0 @@ -# Git -.git -.gitignore -.gitattributes - - -# CI -.codeclimate.yml -.travis.yml -.taskcluster.yml - -# Docker -docker-compose.yml -service_app/Dockerfile -.docker -.dockerignore - -# Byte-compiled / optimized / DLL files -**/__pycache__/ -**/*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -.Python -service_app/env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Virtual environment -service_app/.env -.venv/ -venv/ - -# PyCharm -.idea - -# Python mode for VIM -.ropeproject -**/.ropeproject - -# Vim swap files -**/*.swp - -# VS Code -.vscode/ - -test_application/ - - diff --git a/ApiServices/AuthService/app.py b/ApiServices/AuthService/app.py index 0215eb0..f60580f 100644 --- a/ApiServices/AuthService/app.py +++ b/ApiServices/AuthService/app.py @@ -29,7 +29,7 @@ error_handlers = ErrorHandlers.create( requests=Request, exceptions=HTTPException, response_model=JSONResponse, - status=status + status=status, ) # Register error handlers with bound methods diff --git a/ApiServices/AuthService/application/authentication.py b/ApiServices/AuthService/application/authentication.py new file mode 100644 index 0000000..faac1af --- /dev/null +++ b/ApiServices/AuthService/application/authentication.py @@ -0,0 +1,751 @@ +import json +from typing import Union +from fastapi import status +from fastapi.requests import Request +from fastapi.exceptions import HTTPException + +from api_objects import OccupantTokenObject, EmployeeTokenObject +from api_objects.auth.token_objects import CompanyToken, OccupantToken +from api_services.templates.password_templates import ( + password_is_changed_template, + change_your_password_template, +) +from api_services.token_service import TokenService +from api_services.redis.functions import RedisActions +from api_library.response_handlers import ResponseHandler +from api_library.date_time_actions.date_functions import system_arrow + +# from api_library.user_logger import UserLogger + +from api_validations.validations_request import ( + Login, + Logout, + ChangePassword, + EmployeeSelection, + OccupantSelection, + CreatePassword, + Forgot, + # ResetPassword, + # RefreshToken, +) +from api_validations.validations_response import ( + AuthenticationLoginResponse, + AuthenticationRefreshResponse, + AuthenticationUserInfoResponse, +) +from ApiServices.api_handlers.auth_actions.auth import AuthActions +from api_configs import Auth, ApiStatic +from api_events.events.abstract_class import MethodToEvent, ActionsSchema + +from databases import ( + Companies, + Staff, + Duties, + Departments, + Employees, + BuildLivingSpace, + BuildParts, + Build, + Duty, + Event2Occupant, + Event2Employee, + Users, + UsersTokens, + OccupantTypes, + RelationshipEmployee2Build, +) + +from api_services import ( + send_email, +) + + +class AuthenticationLoginEventMethods(MethodToEvent): + event_type = "LOGIN" + event_description = "Login via domain and access key : [email] | [phone]" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "e672846d-cc45-4d97-85d5-6f96747fac67": "authentication_login_with_domain_and_creds", + } + __event_validation__ = { + "e672846d-cc45-4d97-85d5-6f96747fac67": AuthenticationLoginResponse, + } + + @classmethod + def authentication_login_with_domain_and_creds(cls, data: Login, request: Request): + try: + access_dict = Users.login_user_with_credentials(data=data, request=request) + found_user = access_dict.get("user") + if not found_user: + # UserLogger.log_login_attempt( + # request, + # None, + # data.domain, + # data.access_key, + # success=False, + # error="Invalid credentials", + # ) + return ResponseHandler.unauthorized("Invalid credentials") + + # UserLogger.log_login_attempt( + # request, found_user.id, data.domain, data.access_key, success=True + # ) + response_data = { + "access_token": access_dict.get("access_token"), + "refresh_token": access_dict.get("refresher_token"), + "access_object": access_dict.get("access_object"), + "user": found_user.get_dict(), + } + return ResponseHandler.success( + message="User logged in successfully", + data=response_data, + ) + except Exception as e: + # UserLogger.log_login_attempt( + # request, None, data.domain, data.access_key, success=False, error=str(e) + # ) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) + + +class AuthenticationSelectEventMethods(MethodToEvent): + event_type = "LOGIN" + event_description = "Select Employee Duty or Occupant Type" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type", + } + __event_validation__ = { + "cee96b9b-8487-4e9f-aaed-2e8c79687bf9": "authentication_select_company_or_occupant_type", + } + + @classmethod + def _handle_employee_selection( + cls, data: EmployeeSelection, token_dict: EmployeeTokenObject, request: Request + ): + """Handle employee company selection""" + if data.company_uu_id not in token_dict.companies_uu_id_list: + return ResponseHandler.unauthorized( + "Company not found in user's company list" + ) + + selected_company = Companies.filter_one( + Companies.uu_id == data.company_uu_id + ).data + if not selected_company: + return ResponseHandler.not_found("Company not found") + + # Get department IDs for the company + department_ids = [ + dept.id + for dept in Departments.filter_all( + Departments.company_id == selected_company.id + ).data + ] + + # Get duties IDs for the company + duties_ids = [ + duty.id + for duty in Duties.filter_all(Duties.company_id == selected_company.id).data + ] + + # Get staff IDs + staff_ids = [ + staff.id for staff in Staff.filter_all(Staff.duties_id.in_(duties_ids)).data + ] + + # Get employee + employee = Employees.filter_one( + Employees.people_id == token_dict.person_id, + Employees.staff_id.in_(staff_ids), + ).data + + if not employee: + return ResponseHandler.not_found("Employee not found") + + # Get reachable events + reachable_event_list_id = Event2Employee.get_event_id_by_employee_id( + employee_id=employee.id + ) + + # Get staff and duties + staff = Staff.filter_one(Staff.id == employee.staff_id).data + duties = Duties.filter_one(Duties.id == staff.duties_id).data + department = Departments.filter_one(Departments.id == duties.department_id).data + + # Get bulk duty + bulk_id = Duty.filter_by_one(system=True, duty_code="BULK").data + bulk_duty_id = Duties.filter_by_one( + company_id=selected_company.id, + duties_id=bulk_id.id, + **Duties.valid_record_dict, + ).data + + # Create company token + company_token = CompanyToken( + company_uu_id=selected_company.uu_id.__str__(), + company_id=selected_company.id, + department_id=department.id, + department_uu_id=department.uu_id.__str__(), + duty_id=duties.id, + duty_uu_id=duties.uu_id.__str__(), + bulk_duties_id=bulk_duty_id.id, + staff_id=staff.id, + staff_uu_id=staff.uu_id.__str__(), + employee_id=employee.id, + employee_uu_id=employee.uu_id.__str__(), + reachable_event_list_id=reachable_event_list_id, + ) + + # Update Redis + AuthActions.update_selected_to_redis(request=request, add_payload=company_token) + return ResponseHandler.success("Company selected successfully") + + @classmethod + def _handle_occupant_selection( + cls, data: OccupantSelection, token_dict: OccupantTokenObject, request: Request + ): + """Handle occupant type selection""" + # Get occupant type + occupant_type = OccupantTypes.filter_by_one( + system=True, uu_id=data.occupant_uu_id + ).data + if not occupant_type: + return ResponseHandler.not_found("Occupant Type not found") + + # Get build part + build_part = BuildParts.filter_by_one( + system=True, uu_id=data.build_part_uu_id + ).data + if not build_part: + return ResponseHandler.not_found("Build Part not found") + + # Get build and company info + build = Build.filter_one(Build.id == build_part.build_id).data + related_company = RelationshipEmployee2Build.filter_one( + RelationshipEmployee2Build.member_id == build.id + ).data + company_related = Companies.filter_one( + Companies.id == related_company.company_id + ).data + responsible_employee = Employees.filter_one( + Employees.id == related_company.employee_id + ).data + + # Get selected occupant type + selected_occupant_type = BuildLivingSpace.filter_one( + BuildLivingSpace.occupant_type == occupant_type.id, + BuildLivingSpace.person_id == token_dict.person_id, + BuildLivingSpace.build_parts_id == build_part.id, + ).data + if not selected_occupant_type: + return ResponseHandler.not_found("Selected occupant type not found") + + # Get reachable events + reachable_event_list_id = Event2Occupant.get_event_id_by_build_living_space_id( + build_living_space_id=selected_occupant_type.id + ) + + # Create occupant token + occupant_token = OccupantToken( + living_space_id=selected_occupant_type.id, + living_space_uu_id=selected_occupant_type.uu_id.__str__(), + occupant_type_id=occupant_type.id, + occupant_type_uu_id=occupant_type.uu_id.__str__(), + occupant_type=occupant_type.occupant_type, + build_id=build.id, + build_uuid=build.uu_id.__str__(), + build_part_id=build_part.id, + build_part_uuid=build_part.uu_id.__str__(), + responsible_employee_id=responsible_employee.id, + responsible_employee_uuid=responsible_employee.uu_id.__str__(), + responsible_company_id=company_related.id, + responsible_company_uuid=company_related.uu_id.__str__(), + reachable_event_list_id=reachable_event_list_id, + ) + + # Update Redis + AuthActions.update_selected_to_redis( + request=request, add_payload=occupant_token + ) + return ResponseHandler.success("Occupant selected successfully") + + @classmethod + def authentication_select_company_or_occupant_type( + cls, + request: Request, + data: Union[EmployeeSelection, OccupantSelection], + token_dict: Union[EmployeeTokenObject, OccupantTokenObject], + ): + """Handle selection of company or occupant type""" + try: + if isinstance(token_dict, EmployeeTokenObject): + return cls._handle_employee_selection(data, token_dict, request) + elif isinstance(token_dict, OccupantTokenObject): + return cls._handle_occupant_selection(data, token_dict, request) + return ResponseHandler.error( + "Invalid token type", status_code=status.HTTP_400_BAD_REQUEST + ) + except Exception as e: + return ResponseHandler.error( + str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + +class AuthenticationCheckTokenEventMethods(MethodToEvent): + event_type = "LOGIN" + event_description = "Check Token is valid for user" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid", + } + __event_validation__ = { + "73d77e45-a33f-4f12-909e-3b56f00d8a12": "authentication_check_token_is_valid", + } + + @classmethod + def authentication_check_token_is_valid(cls, request: Request): + try: + if RedisActions.get_object_via_access_key(request=request): + return ResponseHandler.success("Access Token is valid") + except HTTPException: + return ResponseHandler.unauthorized("Access Token is NOT valid") + + +class AuthenticationRefreshEventMethods(MethodToEvent): + event_type = "LOGIN" + event_description = "Refresh user info using access token" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "48379bb2-ba81-4d8e-a9dd-58837cfcbf67": "authentication_refresh_user_info", + } + __event_validation__ = { + "48379bb2-ba81-4d8e-a9dd-58837cfcbf67": AuthenticationRefreshResponse, + } + + @classmethod + def authentication_refresh_user_info( + cls, + request: Request, + token_dict: Union[EmployeeTokenObject, OccupantTokenObject], + ): + try: + access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) + if not access_token: + return ResponseHandler.unauthorized() + + # Get user and token info + found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data + if not found_user: + return ResponseHandler.not_found("User not found") + user_token = UsersTokens.filter_one( + UsersTokens.domain == found_user.domain_name, + UsersTokens.user_id == found_user.id, + UsersTokens.token_type == "RememberMe", + ).data + response_data = { + "access_token": access_token, + "refresh_token": getattr(user_token, "token", None), + "user": found_user.get_dict(), + } + return ResponseHandler.success( + "User info refreshed successfully", + data=response_data, + ) + except Exception as e: + return ResponseHandler.error(str(e)) + + +class AuthenticationChangePasswordEventMethods(MethodToEvent): + event_type = "LOGIN" + event_description = "Change password with access token" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password", + } + __event_validation__ = { + "f09f7c1a-bee6-4e32-8444-962ec8f39091": "authentication_change_password", + } + + @classmethod + def authentication_change_password( + cls, + request: Request, + data: ChangePassword, + token_dict: Union[EmployeeTokenObject, OccupantTokenObject], + ): + try: + if not isinstance(token_dict, EmployeeTokenObject): + return ResponseHandler.unauthorized( + "Only employees can change password" + ) + + found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data + if not found_user: + return ResponseHandler.not_found("User not found") + + if not found_user.check_password(data.old_password): + # UserLogger.log_password_change( + # request, + # found_user.id, + # "change", + # success=False, + # error="Invalid old password", + # ) + return ResponseHandler.unauthorized("Old password is incorrect") + + found_user.set_password(data.new_password) + # UserLogger.log_password_change( + # request, found_user.id, "change", success=True + # ) + + return ResponseHandler.success("Password changed successfully") + except Exception as e: + # UserLogger.log_password_change( + # request, + # found_user.id if found_user else None, + # "change", + # success=False, + # error=str(e), + # ) + return ResponseHandler.error(str(e)) + + +class AuthenticationCreatePasswordEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Create password with password reset token requested via email" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password", + } + __event_validation__ = { + "c519f9af-92e1-47b2-abf7-5a3316d075f7": "authentication_create_password", + } + + @classmethod + def authentication_create_password(cls, data: CreatePassword): + + if not data.re_password == data.password: + raise HTTPException( + status_code=status.HTTP_406_NOT_ACCEPTABLE, detail="Password must match" + ) + if found_user := Users.filter_one( + Users.password_token == data.password_token + ).data: + found_user: Users = found_user + found_user.create_password(found_user=found_user, password=data.password) + found_user.password_token = "" + found_user.save() + # send_email_completed = send_email( + # subject=f"Dear {found_user.user_tag}, your password has been changed.", + # receivers=[str(found_user.email)], + # html=password_is_changed_template(user_name=found_user.user_tag), + # ) + # if not send_email_completed: + # raise HTTPException( + # status_code=400, detail="Email can not be sent. Try again later" + # ) + return ResponseHandler.success( + "Password is created successfully", + data=found_user.get_dict(), + ) + return ResponseHandler.not_found("Record not found") + + +class AuthenticationDisconnectUserEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Disconnect all sessions of user in access token" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user", + } + __event_validation__ = { + "8b586848-2fb3-4161-abbe-642157eec7ce": "authentication_disconnect_user", + } + + @classmethod + def authentication_disconnect_user( + cls, data: Logout, token_dict: Union[EmployeeTokenObject, OccupantTokenObject] + ): + found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data + if not found_user: + return ResponseHandler.not_found("User not found") + if already_tokens := RedisActions.get_object_via_user_uu_id( + user_id=str(found_user.uu_id) + ): + for key, token_user in already_tokens.items(): + RedisActions.delete(key) + selected_user = Users.filter_one( + Users.uu_id == token_user.get("uu_id"), + ).data + selected_user.remove_refresher_token( + domain=data.domain, disconnect=True + ) + return ResponseHandler.success( + "All sessions are disconnected", + data=selected_user.get_dict(), + ) + return ResponseHandler.not_found("Invalid data") + + +class AuthenticationLogoutEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Logout only single session of user which domain is provided" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user", + } + __event_validation__ = { + "5cc22e4e-a0f7-4077-be41-1871feb3dfd1": "authentication_logout_user", + } + + @classmethod + def authentication_logout_user( + cls, request: Request, data: Logout, token_dict: dict = None + ): + token_user = None + if already_tokens := RedisActions.get_object_via_access_key(request=request): + for key in already_tokens: + token_user = RedisActions.get_json(key) + if token_user.get("domain") == data.domain: + RedisActions.delete(key) + selected_user = Users.filter_one( + Users.uu_id == token_user.get("uu_id"), + ).data + selected_user.remove_refresher_token(domain=data.domain) + + return ResponseHandler.success( + "Session is logged out", + data=token_user, + ) + return ResponseHandler.not_found("Logout is not successfully completed") + + +class AuthenticationRefreshTokenEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Refresh access token with refresher token" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "c90f3334-10c9-4181-b5ff-90d98a0287b2": "authentication_refresher_token", + } + __event_validation__ = { + "c90f3334-10c9-4181-b5ff-90d98a0287b2": AuthenticationRefreshResponse, + } + + @classmethod + def authentication_refresher_token( + # cls, request: Request, data: RefreshToken, token_dict: dict = None + cls, + request: Request, + data, + token_dict: dict = None, + ): + token_refresher = UsersTokens.filter_by_one( + token=data.refresh_token, + domain=data.domain, + **UsersTokens.valid_record_dict, + ).data + if not token_refresher: + return ResponseHandler.not_found("Invalid data") + if found_user := Users.filter_one( + Users.id == token_refresher.user_id, + ).data: + found_user: Users = found_user + access_key = AuthActions.save_access_token_to_redis( + request=request, found_user=found_user, domain=data.domain + ) + found_user.last_agent = request.headers.get("User-Agent", None) + found_user.last_platform = request.headers.get("Origin", None) + found_user.last_remote_addr = getattr( + request, "remote_addr", None + ) or request.headers.get("X-Forwarded-For", None) + found_user.last_seen = str(system_arrow.now()) + response_data = { + "access_token": access_key, + "refresh_token": data.refresh_token, + } + return ResponseHandler.success( + "User is logged in successfully via refresher token", + data=response_data, + ) + return ResponseHandler.not_found("Invalid data") + + +class AuthenticationForgotPasswordEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Send an email to user for a valid password reset token" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password", + } + __event_validation__ = { + "e3ca6e24-b9f8-4127-949c-3bfa364e3513": "authentication_forgot_password", + } + + @classmethod + def authentication_forgot_password( + cls, + request: Request, + data: Forgot, + ): + found_user: Users = Users.check_user_exits( + access_key=data.access_key, domain=data.domain + ) + forgot_key = AuthActions.save_access_token_to_redis( + request=request, found_user=found_user, domain=data.domain + ) + forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key) + send_email_completed = send_email( + subject=f"Dear {found_user.user_tag}, your forgot password link has been sent.", + receivers=[str(found_user.email)], + html=change_your_password_template( + user_name=found_user.user_tag, forgot_link=forgot_link + ), + ) + if not send_email_completed: + raise HTTPException( + status_code=400, detail="Email can not be sent. Try again later" + ) + found_user.password_token = forgot_key + found_user.password_token_is_valid = str(system_arrow.shift(days=1)) + found_user.save() + + return ResponseHandler.success( + "Password is change link is sent to your email or phone", + data={}, + ) + + +class AuthenticationResetPasswordEventMethods(MethodToEvent): + + event_type = "UPDATE" + __event_keys__ = { + "af9e121e-24bb-44ac-a616-471d5754360e": "authentication_reset_password", + } + + @classmethod + def authentication_reset_password(cls, data: Forgot): + from sqlalchemy import or_ + + found_user = Users.query.filter( + or_( + Users.email == str(data.access_key).lower(), + Users.phone_number == str(data.access_key).replace(" ", ""), + ), + ).first() + if not found_user: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Given access key or domain is not matching with the any user record.", + ) + + reset_password_token = found_user.reset_password_token(found_user=found_user) + send_email_completed = send_email( + subject=f"Dear {found_user.user_tag}, a password reset request has been received.", + receivers=[str(found_user.email)], + html=change_your_password_template( + user_name=found_user.user_tag, + forgot_link=ApiStatic.forgot_link(forgot_key=reset_password_token), + ), + ) + if not send_email_completed: + raise found_user.raise_http_exception( + status_code=400, message="Email can not be sent. Try again later" + ) + return ResponseHandler.success( + "Password change link is sent to your email or phone", + data=found_user.get_dict(), + ) + + +class AuthenticationDownloadAvatarEventMethods(MethodToEvent): + + event_type = "LOGIN" + event_description = "Download avatar icon and profile info of user" + event_category = "AUTHENTICATION" + + __event_keys__ = { + "c140cd5f-307f-4046-a93e-3ade032a57a7": "authentication_download_avatar", + } + __event_validation__ = { + "c140cd5f-307f-4046-a93e-3ade032a57a7": AuthenticationUserInfoResponse, + } + + @classmethod + def authentication_download_avatar( + cls, token_dict: Union[EmployeeTokenObject, OccupantTokenObject] + ): + if found_user := Users.filter_one(Users.id == token_dict.user_id).data: + expired_starts = str( + system_arrow.now() - system_arrow.get(str(found_user.expiry_ends)) + ) + expired_int = ( + system_arrow.now() - system_arrow.get(str(found_user.expiry_ends)) + ).days + + user_info = { + "lang": token_dict.lang, + "full_name": found_user.person.full_name, + "avatar": found_user.avatar, + "remember_me": found_user.remember_me, + "expiry_ends": str(found_user.expiry_ends), + "expired_str": expired_starts, + "expired_int": int(expired_int), + } + return ResponseHandler.success( + "Avatar and profile is shared via user credentials", + data=user_info, + ) + return ResponseHandler.not_found("Invalid data") + + +AuthenticationLoginEventMethod = AuthenticationLoginEventMethods( + action=ActionsSchema(endpoint="/authentication/login") +) +AuthenticationSelectEventMethod = AuthenticationSelectEventMethods( + action=ActionsSchema(endpoint="/authentication/select") +) +AuthenticationCheckTokenEventMethod = AuthenticationCheckTokenEventMethods( + action=ActionsSchema(endpoint="/authentication/valid") +) +AuthenticationRefreshEventMethod = AuthenticationRefreshEventMethods( + action=ActionsSchema(endpoint="/authentication/refresh") +) +AuthenticationChangePasswordEventMethod = AuthenticationChangePasswordEventMethods( + action=ActionsSchema(endpoint="/authentication/change_password") +) +AuthenticationCreatePasswordEventMethod = AuthenticationCreatePasswordEventMethods( + action=ActionsSchema(endpoint="/authentication/create_password") +) +AuthenticationDisconnectUserEventMethod = AuthenticationDisconnectUserEventMethods( + action=ActionsSchema(endpoint="/authentication/disconnect") +) +AuthenticationLogoutEventMethod = AuthenticationLogoutEventMethods( + action=ActionsSchema(endpoint="/authentication/logout") +) +AuthenticationRefreshTokenEventMethod = AuthenticationRefreshTokenEventMethods( + action=ActionsSchema(endpoint="/authentication/refresher") +) +AuthenticationForgotPasswordEventMethod = AuthenticationForgotPasswordEventMethods( + action=ActionsSchema(endpoint="/authentication/forgot") +) +AuthenticationDownloadAvatarEventMethod = AuthenticationDownloadAvatarEventMethods( + action=ActionsSchema(endpoint="/authentication/avatar") +) +AuthenticationResetPasswordEventMethod = AuthenticationResetPasswordEventMethods( + action=ActionsSchema(endpoint="/authentication/reset_password") +) diff --git a/ApiServices/AuthService/middlewares/token_middleware.py b/ApiServices/AuthService/middlewares/token_middleware.py index 6e2b20d..7e89870 100644 --- a/ApiServices/AuthService/middlewares/token_middleware.py +++ b/ApiServices/AuthService/middlewares/token_middleware.py @@ -15,8 +15,8 @@ class MiddlewareLogs: def log_middlewares_exception(endpoint, token_user, message, request): - MiddlewareLogs.log_error( - str( + MiddlewareLogs().log_error( + log_message=json.dumps( { "log_type": "Authentication", "log_message": message, @@ -29,7 +29,7 @@ def log_middlewares_exception(endpoint, token_user, message, request): } ), } - ) + ), ) @@ -72,9 +72,9 @@ def check_if_path_secure(request, insecure_paths) -> bool: def check_if_token_is_not_valid(request, endpoint_name): - from api_services.redis.functions import get_object_via_access_key + from api_services.redis.functions import RedisActions - token_user = get_object_via_access_key(request) + token_user = RedisActions.get_object_via_access_key(request) if not token_user: return "Session geçerli değil. Lütfen tekrar giriş yapınız.", token_user diff --git a/ApiServices/AuthService/pyproject.toml b/ApiServices/AuthService/pyproject.toml index f105854..28145a8 100644 --- a/ApiServices/AuthService/pyproject.toml +++ b/ApiServices/AuthService/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "wag-managment-api-service-version-3" +name = "wag-managment-api-service-version-3-auth-service" version = "0.1.0" description = "Wag Python API Service" readme = "README.md" diff --git a/ApiServices/AuthService/routers/authentication/router.py b/ApiServices/AuthService/routers/authentication/router.py index e1343dd..c82f4eb 100644 --- a/ApiServices/AuthService/routers/authentication/router.py +++ b/ApiServices/AuthService/routers/authentication/router.py @@ -12,7 +12,7 @@ from api_validations.validations_request import ( OccupantSelection, EmployeeSelection, ) -from api_events.events import ( +from ApiServices.AuthService.application.authentication import ( AuthenticationLoginEventMethod, AuthenticationSelectEventMethod, AuthenticationCheckTokenEventMethod, diff --git a/ApiServices/EventService/Dockerfile b/ApiServices/EventService/Dockerfile index e927e0c..c5e8ace 100644 --- a/ApiServices/EventService/Dockerfile +++ b/ApiServices/EventService/Dockerfile @@ -2,35 +2,35 @@ FROM python:3.12-slim-bookworm ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 +ENV PYTHONPATH=/service_app COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ +WORKDIR /service_app + # Create logs directory RUN mkdir -p /service_app/logs COPY ApiServices/EventService/pyproject.toml . -RUN uv venv -RUN uv pip install -r pyproject.toml +RUN uv venv .venv +RUN . .venv/bin/activate && uv pip install -r pyproject.toml -COPY ApiServices/EventService ./service_app -COPY ApiServices/api_handlers ./service_app/api_handlers +COPY ApiServices ./ApiServices +COPY databases ./databases +COPY api_services ./api_services +COPY api_objects ./api_objects +COPY api_configs ./api_configs +COPY api_events ./api_events +COPY api_library ./api_library +COPY api_validations ./api_validations -COPY databases ./service_app/databases -COPY api_services ./service_app/api_services -COPY api_objects ./service_app/api_objects -COPY api_configs ./service_app/api_configs -COPY api_events ./service_app/api_events -COPY api_library ./service_app/api_library -COPY api_validations ./service_app/api_validations +WORKDIR /service_app/ApiServices/EventService -WORKDIR /service_app +# Create startup script +RUN echo '#!/bin/bash\n\ +source /service_app/.venv/bin/activate\n\ +exec python app.py' > /service_app/start.sh && \ + chmod +x /service_app/start.sh -CMD ["uv", "run", "app.py"] - -# Old File -#FROM python:3.10 - -#RUN pip install --upgrade pip -#RUN pip install --no-cache-dir --upgrade -r requirements.txt -#CMD ["python", "-m", "app"] +CMD ["/service_app/start.sh"] diff --git a/ApiServices/EventService/app.py b/ApiServices/EventService/app.py index 7c3e4f7..f60580f 100644 --- a/ApiServices/EventService/app.py +++ b/ApiServices/EventService/app.py @@ -2,7 +2,8 @@ import uvicorn import routers from fastapi.middleware.cors import CORSMiddleware -from fastapi.exceptions import HTTPException +from fastapi import Request, HTTPException, status +from fastapi.responses import JSONResponse from middlewares.token_middleware import AuthHeaderMiddleware from application.create_file import create_app @@ -23,8 +24,17 @@ app.add_middleware( ) app.add_middleware(AuthHeaderMiddleware) -app.add_exception_handler(HTTPException, ErrorHandlers.exception_handler_http) -app.add_exception_handler(Exception, ErrorHandlers.exception_handler_exception) +# Initialize error handlers +error_handlers = ErrorHandlers.create( + requests=Request, + exceptions=HTTPException, + response_model=JSONResponse, + status=status, +) + +# Register error handlers with bound methods +app.add_exception_handler(HTTPException, error_handlers.exception_handler_http) +app.add_exception_handler(Exception, error_handlers.exception_handler_exception) if __name__ == "__main__": uvicorn_config = { diff --git a/ApiServices/EventService/middlewares/token_middleware.py b/ApiServices/EventService/middlewares/token_middleware.py index 6e2b20d..dc26788 100644 --- a/ApiServices/EventService/middlewares/token_middleware.py +++ b/ApiServices/EventService/middlewares/token_middleware.py @@ -72,9 +72,9 @@ def check_if_path_secure(request, insecure_paths) -> bool: def check_if_token_is_not_valid(request, endpoint_name): - from api_services.redis.functions import get_object_via_access_key + from api_services.redis.functions import RedisActions - token_user = get_object_via_access_key(request) + token_user = RedisActions.get_object_via_access_key(request) if not token_user: return "Session geçerli değil. Lütfen tekrar giriş yapınız.", token_user diff --git a/ApiServices/EventService/pyproject.toml b/ApiServices/EventService/pyproject.toml index f105854..9916b02 100644 --- a/ApiServices/EventService/pyproject.toml +++ b/ApiServices/EventService/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "wag-managment-api-service-version-3" +name = "wag-managment-api-service-version-3-event-service" version = "0.1.0" description = "Wag Python API Service" readme = "README.md" diff --git a/ApiServices/EventService/routers/__init__.py b/ApiServices/EventService/routers/__init__.py index a9a2c5b..c74a3c4 100644 --- a/ApiServices/EventService/routers/__init__.py +++ b/ApiServices/EventService/routers/__init__.py @@ -1 +1,101 @@ -__all__ = [] +from .account.router import account_records_router +from .people.router import people_router +from .users.router import user_route + +from .company.company.router import company_route +from .company.department.router import department_route +from .company.duty.router import duty_route +from .company.duties.router import duties_route + +from .building.build.router import build_route +from .building.buildparts.router import build_parts_route +from .building.buildarea.router import build_area_route +from .building.buildsites.router import build_sites_route +from .building.buildtypes.router import build_types_route +from .building.livingspaces.router import build_living_space + +from .decision_book.decision_book.router import build_decision_book_route + +from .decision_book.decision_book_person.router import ( + build_decision_book_people_route, +) +from .decision_book.decision_book_items.router import ( + build_decision_book_items_route, +) +from .project_decision_book.project_decision_book.router import ( + build_project_decision_book_route, +) +from .project_decision_book.project_decision_book_person.router import ( + build_project_decision_book_person_route, +) +from .api.router import internal_route +from .events.events.router import event_route +from .company.staff.router import staff_route +from .company.employee.router import employee_route + +from .events.events.bind_events_router import bind_events_route + +from .events.modules.router import modules_route +from .events.modules.bind_events_router import bind_modules_route + +from .events.services.bind_services_router import bind_services_route +from .events.services.router import services_route + +from .rules.router import endpoint_restriction_route +from .address.address.router import address_router +from .address.post_code.router import post_code_router +from .application.enums_and_drops.router import enums_route +from .application.occupants.router import occupant_types_route +from .decision_book.decision_book_invitations.router import ( + build_decision_book_invitations, +) +from .decision_book.project_decision_book.router import ( + build_decision_book_project_route, +) +from .decision_book.project_decision_book_items.router import ( + build_decision_book_project_items_route, +) +from .decision_book.project_decision_book_person.router import ( + build_decision_book_project_people_route, +) +from .validations.router import validations_route + +__all__ = [ + "account_records_router", + "enums_route", + "occupant_types_route", + "internal_route", + "address_router", + "post_code_router", + "duty_route", + "duties_route", + "people_router", + "user_route", + "company_route", + "department_route", + "build_route", + "build_parts_route", + "build_sites_route", + "build_area_route", + "build_living_space", + "build_decision_book_route", + "build_decision_book_people_route", + "build_decision_book_items_route", + "build_project_decision_book_route", + "staff_route", + "employee_route", + "build_types_route", + "bind_events_route", + "event_route", + "modules_route", + "bind_services_route", + "bind_modules_route", + "services_route", + "build_project_decision_book_person_route", + "endpoint_restriction_route", + "build_decision_book_invitations", + "build_decision_book_project_route", + "build_decision_book_project_items_route", + "build_decision_book_project_people_route", + "validations_route", +] diff --git a/ApiServices/EventService/routers/account/router.py b/ApiServices/EventService/routers/account/router.py new file mode 100644 index 0000000..d26d697 --- /dev/null +++ b/ApiServices/EventService/routers/account/router.py @@ -0,0 +1,57 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertAccountRecord, + UpdateAccountRecord, + SearchAddress, + ListOptions, + PatchRecord, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +account_records_router = APIRouter(prefix="/account/records", tags=["Account Records"]) +account_records_router.include_router(account_records_router, include_in_schema=True) + + +@account_records_router.post(path="/list", summary="List Active/Delete/Confirm Address") +def address_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@account_records_router.post( + path="/create", summary="Create Address with given auth levels" +) +def address_create(request: Request, data: InsertAccountRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@account_records_router.post( + path="/search", summary="Search Address with given auth levels" +) +def address_search(request: Request, data: SearchAddress): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@account_records_router.post( + path="/update/{address_uu_id}", summary="Update Address with given auth levels" +) +def address_update(request: Request, address_uu_id: str, data: UpdateAccountRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, address_uu_id=address_uu_id, token_dict=token_dict + ) + + +@account_records_router.patch( + path="/patch/{address_uu_id}", summary="Update Address Active/Delete/Confirm" +) +def address_patch(request: Request, address_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, address_uu_id=address_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/address/address/router.py b/ApiServices/EventService/routers/address/address/router.py new file mode 100644 index 0000000..6a09483 --- /dev/null +++ b/ApiServices/EventService/routers/address/address/router.py @@ -0,0 +1,54 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertAddress, + UpdateAddress, + SearchAddress, + ListOptions, + PatchRecord, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +address_router = APIRouter(prefix="/address", tags=["Address"]) +address_router.include_router(address_router, include_in_schema=True) + + +@address_router.post(path="/list", summary="List Active/Delete/Confirm Address") +def address_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@address_router.post(path="/create", summary="Create Address with given auth levels") +def address_create(request: Request, data: InsertAddress): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@address_router.post(path="/search", summary="Search Address with given auth levels") +def address_search(request: Request, data: SearchAddress): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@address_router.post( + path="/update/{address_uu_id}", summary="Update Address with given auth levels" +) +def address_update(request: Request, address_uu_id: str, data: UpdateAddress): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, address_uu_id=address_uu_id, token_dict=token_dict + ) + + +@address_router.patch( + path="/patch/{address_uu_id}", summary="Update Address Active/Delete/Confirm" +) +def address_patch(request: Request, address_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, address_uu_id=address_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/address/post_code/router.py b/ApiServices/EventService/routers/address/post_code/router.py new file mode 100644 index 0000000..b40ba63 --- /dev/null +++ b/ApiServices/EventService/routers/address/post_code/router.py @@ -0,0 +1,46 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertPostCode, + UpdatePostCode, + ListOptions, + PatchRecord, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +post_code_router = APIRouter(prefix="/postcode", tags=["Post Code"]) +post_code_router.include_router(post_code_router, include_in_schema=True) + + +@post_code_router.post(path="/list", summary="List Active/Delete/Confirm PostCode") +def post_code_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@post_code_router.post(path="/create", summary="Create PostCode with given auth levels") +def post_code_create(request: Request, data: InsertPostCode): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@post_code_router.post( + path="/update/{post_code_uu_id}", summary="Update PostCode with given auth levels" +) +def post_code_update(request: Request, post_code_uu_id: str, data: UpdatePostCode): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, post_code_uu_id=post_code_uu_id, token_dict=token_dict + ) + + +@post_code_router.patch( + path="/patch/{post_code_uu_id}", summary="Update PostCode Active/Delete/Confirm" +) +def post_code_patch(request: Request, post_code_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, post_code_uu_id=post_code_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/api/router.py b/ApiServices/EventService/routers/api/router.py new file mode 100644 index 0000000..19b136c --- /dev/null +++ b/ApiServices/EventService/routers/api/router.py @@ -0,0 +1,74 @@ +import json +import typing +import zlib +from base64 import b64decode + +from fastapi import status +from fastapi.routing import APIRouter +from fastapi.responses import JSONResponse +from fastapi.requests import Request + +from pydantic import BaseModel + + +internal_route = APIRouter(prefix="/internal", tags=["Internal"]) +internal_route.include_router(internal_route, include_in_schema=False) + + +# class ApiReceive(BaseModel): +# data: str +# +# +# class BankReceive(BaseModel): +# import_file_name: str +# iban: str +# bank_date: str +# channel_branch: str +# currency: typing.Optional[str] = "TL" +# currency_value: float +# bank_balance: float +# additional_balance: float +# process_name: str +# process_type: str +# process_comment: str +# bank_reference_code: str +# # +# +# @internal_route.post( +# path="/isbank/retreive", +# summary="Receive isbank xls service from mail reader service", +# ) +# def is_bank_retrieve_account_records(request: Request, bank_data: ApiReceive): +# from databases import AccountRecords +# +# data_dict = bank_data.model_dump() +# data_bulk = json.loads(zlib.decompress(b64decode(data_dict["data"]))) +# print("data_bulk", data_bulk) +# new_record_list = [] +# for data_keys in data_bulk: # data_bulk is a dict +# for data_dict in data_bulk[data_keys]: # data_bulk[data_keys] is a list +# data_dict["bank_balance"] = data_dict.pop("balance") +# data_dict["import_file_name"] = str(data_keys) +# print("data_dict before pyd", data_dict) +# data_dict = BankReceive(**data_dict).model_dump() +# print("data_dict after pyd", data_dict) +# if new_account_record := AccountRecords.find_or_create(**data_dict): +# print("new_account_record.is_found", new_account_record.is_found) +# if not new_account_record.is_found: +# new_record_list.append(new_account_record.get_dict()) +# if new_record_list: +# return JSONResponse( +# content={ +# "completed": True, +# "message": "Create Bank Record", +# "data": new_record_list, +# }, +# status_code=status.HTTP_200_OK, +# ) +# return JSONResponse( +# content={ +# "completed": False, +# "message": "Record already exist or can not be created", +# }, +# status_code=status.HTTP_406_NOT_ACCEPTABLE, +# ) diff --git a/ApiServices/EventService/routers/application/enums_and_drops/router.py b/ApiServices/EventService/routers/application/enums_and_drops/router.py new file mode 100644 index 0000000..bb80544 --- /dev/null +++ b/ApiServices/EventService/routers/application/enums_and_drops/router.py @@ -0,0 +1,98 @@ +from fastapi import status +from fastapi.routing import APIRouter +from fastapi.exceptions import HTTPException +from fastapi.responses import JSONResponse + +from databases import ApiEnumDropdown +from api_validations.validations_request import ( + SingleEnumClassKey, + SingleEnumUUID, + SingleEnumOnlyClass, +) + + +enums_route = APIRouter(prefix="/enums", tags=["Enums and Dropdowns of API"]) +enums_route.include_router(enums_route, include_in_schema=False) + + +@enums_route.post(path="/get/key", summary="Get single enum via key") +def get_single_enum_via_key_and_class(data: SingleEnumClassKey): + enum = ApiEnumDropdown.query.filter( + ApiEnumDropdown.enum_class.ilike(data.class_name), + ApiEnumDropdown.key.ilike(data.key_name), + ).populate_existing() + if record := enum.first(): + return JSONResponse( + content={ + "completed": True, + "message": "Get single enum via key", + "data": record.get_enum_dict() if enum else None, + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Enum not found", + ) + + +@enums_route.post(path="/get/uu_id", summary="Get single enum via uu_id") +def get_single_enum_via_uuid(data: SingleEnumUUID): + enum = ApiEnumDropdown.query.filter( + ApiEnumDropdown.uu_id == data.uu_id + ).populate_existing() + if records := enum.first(): + return JSONResponse( + content={ + "completed": True, + "message": "Get single enum via uu_id", + "data": records.get_enum_dict(), + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Enum not found", + ) + + +@enums_route.post(path="/list/class", summary="Get all enums via class") +def list_all_enums_with_class(data: SingleEnumOnlyClass): + enums = ApiEnumDropdown.query.filter( + ApiEnumDropdown.enum_class.ilike(data.class_name or "") + ).populate_existing() + if records := enums.all(): + records_list = [record.get_enum_dict() for record in records] + return JSONResponse( + content={ + "completed": True, + "message": "Get all enums via class", + "count": len(records_list), + "data": records_list, + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Enums not found", + ) + + +@enums_route.post(path="/list/all", summary="Get all enums") +def list_all_enums(): + enums = ApiEnumDropdown.query.filter().populate_existing() + if records := enums.all(): + records_list = [record.get_enum_dict() for record in records] + return JSONResponse( + content={ + "completed": True, + "count": len(records_list), + "message": "Get all enums", + "data": records_list, + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Enums can not be listed", + ) diff --git a/ApiServices/EventService/routers/application/occupants/router.py b/ApiServices/EventService/routers/application/occupants/router.py new file mode 100644 index 0000000..3198b68 --- /dev/null +++ b/ApiServices/EventService/routers/application/occupants/router.py @@ -0,0 +1,58 @@ +from fastapi import status +from fastapi.routing import APIRouter +from fastapi.exceptions import HTTPException +from fastapi.responses import JSONResponse + +from databases import OccupantTypes +from api_validations.validations_request import ( + SingleOccupantTypeClassKey, + SingleOccupantTypeUUID, +) + + +occupant_types_route = APIRouter(prefix="/occupant_types", tags=["Occupant Types"]) +occupant_types_route.include_router(occupant_types_route, include_in_schema=False) + + +@occupant_types_route.post( + path="/get/code", summary="Get single occupant type via code" +) +def get_single_occupant_type_via_code(data: SingleOccupantTypeClassKey): + occupant_type = OccupantTypes.query.filter( + OccupantTypes.occupant_code.ilike(data.type_code) + ).populate_existing() + if record := occupant_type.first(): + return JSONResponse( + content={ + "completed": True, + "message": "Get single occupant type via code", + "data": record.get_dict() if record else None, + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Occupant type not found", + ) + + +@occupant_types_route.post( + path="/get/uu_id", summary="Get single occupant type via uu_id" +) +def get_single_occupant_type_via_uuid(data: SingleOccupantTypeUUID): + occupant_type = OccupantTypes.query.filter( + OccupantTypes.uu_id == data.uu_id + ).populate_existing() + if records := occupant_type.first(): + return JSONResponse( + content={ + "completed": True, + "message": "Get single occupant type via uu_id", + "data": records.get_dict(), + }, + status_code=status.HTTP_200_OK, + ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Occupant type not found", + ) diff --git a/ApiServices/__init__.py b/ApiServices/EventService/routers/application/router.py similarity index 100% rename from ApiServices/__init__.py rename to ApiServices/EventService/routers/application/router.py diff --git a/ApiServices/EventService/routers/building/buildarea/router.py b/ApiServices/EventService/routers/building/buildarea/router.py new file mode 100644 index 0000000..d8c3ac4 --- /dev/null +++ b/ApiServices/EventService/routers/building/buildarea/router.py @@ -0,0 +1,49 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildArea, + UpdateBuildArea, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +build_area_route = APIRouter(prefix="/building/area", tags=["Building Area"]) +build_area_route.include_router(build_area_route, include_in_schema=True) + + +@build_area_route.post(path="/list", summary="List Active/Delete/Confirm Build Parts") +def build_area_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_area_route.post( + path="/create", summary="Create BuildParts with given auth levels" +) +def build_area_create(request: Request, data: InsertBuildArea): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_area_route.post( + path="/update/{build_uu_id}", summary="Update BuildParts with given auth levels" +) +def build_area_update(request: Request, build_uu_id: str, data: UpdateBuildArea): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) + + +@build_area_route.patch( + path="/patch/{build_uu_id}", summary="Update Active/Delete/Confirm" +) +def build_area_patch(request: Request, build_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/building/buildparts/router.py b/ApiServices/EventService/routers/building/buildparts/router.py new file mode 100644 index 0000000..31fec07 --- /dev/null +++ b/ApiServices/EventService/routers/building/buildparts/router.py @@ -0,0 +1,50 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildParts, + UpdateBuildParts, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_parts_route = APIRouter(prefix="/building/parts", tags=["Building Parts"]) +build_parts_route.include_router(build_parts_route, include_in_schema=True) + + +@build_parts_route.post(path="/list", summary="List Active/Delete/Confirm Build Parts") +def building_build_part_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_parts_route.post( + path="/create", summary="Create Build Parts with given auth levels" +) +def building_build_part_create(request: Request, data: InsertBuildParts): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_parts_route.post( + path="/update/{build_uu_id}", summary="Update Build Parts with given auth levels" +) +def building_build_part_update( + request: Request, build_uu_id: str, data: UpdateBuildParts +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) + + +@build_parts_route.patch( + path="/patch/{build_uu_id}", summary="Update Active/Delete/Confirm" +) +def building_build_part_patch(request: Request, build_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/building/buildsites/router.py b/ApiServices/EventService/routers/building/buildsites/router.py new file mode 100644 index 0000000..c7b07a8 --- /dev/null +++ b/ApiServices/EventService/routers/building/buildsites/router.py @@ -0,0 +1,48 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + PatchRecord, + ListOptions, + InsertBuildSites, + UpdateBuildSites, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_sites_route = APIRouter(prefix="/building/sites", tags=["Building Sites"]) +build_sites_route.include_router(build_sites_route, include_in_schema=True) + + +@build_sites_route.post(path="/list", summary="List Active/Delete/Confirm Build Parts") +def building_sites_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_sites_route.post( + path="/create", summary="Create Build Sites with given auth levels" +) +def building_sites_create(request: Request, data: InsertBuildSites): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_sites_route.post( + path="/update/{build_uu_id}", summary="Update Build Sites with given auth levels" +) +def building_sites_update(request: Request, build_uu_id: str, data: UpdateBuildSites): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) + + +@build_sites_route.patch( + path="/patch/{build_uu_id}", summary="Update Active/Delete/Confirm" +) +def building_sites_patch(request: Request, build_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/building/buildtypes/router.py b/ApiServices/EventService/routers/building/buildtypes/router.py new file mode 100644 index 0000000..e17c37f --- /dev/null +++ b/ApiServices/EventService/routers/building/buildtypes/router.py @@ -0,0 +1,48 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildTypes, + UpdateBuildTypes, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_types_route = APIRouter(prefix="/building/types", tags=["Types"]) +build_types_route.include_router(build_types_route, include_in_schema=True) + + +@build_types_route.post(path="/list", summary="List Active/Delete/Confirm Build Parts") +def building_types_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_types_route.post( + path="/create", summary="Create BuildParts with given auth levels" +) +def building_types_create(request: Request, data: InsertBuildTypes): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_types_route.post( + path="/update/{build_uu_id}", summary="Update BuildParts with given auth levels" +) +def building_types_update(request: Request, build_uu_id: str, data: UpdateBuildTypes): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) + + +@build_types_route.patch( + path="/patch/{build_uu_id}", summary="Update Active/Delete/Confirm" +) +def building_types_patch(request: Request, build_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/building/livingspaces/router.py b/ApiServices/EventService/routers/building/livingspaces/router.py new file mode 100644 index 0000000..a41662c --- /dev/null +++ b/ApiServices/EventService/routers/building/livingspaces/router.py @@ -0,0 +1,53 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildLivingSpace, + UpdateBuildLivingSpace, + PatchRecord, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_living_space = APIRouter(prefix="/building/living_space", tags=["Living Space"]) +build_living_space.include_router(build_living_space, include_in_schema=True) + + +@build_living_space.post( + path="/list", summary="List Active/Delete/Confirm Build Living Space" +) +def building_living_space_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_living_space.post( + path="/create", summary="Create Build Living Space with given auth levels" +) +def building_living_space_create(request: Request, data: InsertBuildLivingSpace): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_living_space.post( + path="/update/{build_uu_id}", + summary="Update Build Living Space with given auth levels", +) +def building_living_space_update( + request: Request, build_uu_id: str, data: UpdateBuildLivingSpace +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) + + +@build_living_space.patch( + path="/patch/{build_uu_id}", + summary="Update Build Living Space with given auth levels", +) +def building_living_space_patch(request: Request, build_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, build_uu_id=build_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/company/company/router.py b/ApiServices/EventService/routers/company/company/router.py new file mode 100644 index 0000000..54fa1ed --- /dev/null +++ b/ApiServices/EventService/routers/company/company/router.py @@ -0,0 +1,46 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertCompany, + UpdateCompany, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +company_route = APIRouter(prefix="/company", tags=["Company"]) +company_route.include_router(company_route, include_in_schema=True) + + +@company_route.post(path="/list", summary="List Active/Delete/Confirm Companies") +def company_company_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@company_route.post(path="/create", summary="Create Company with given auth levels") +def company_company_create(request: Request, data: InsertCompany): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@company_route.post( + path="/update/{company_uu_id}", summary="Update Company with given auth levels" +) +def company_company_update(request: Request, company_uu_id: str, data: UpdateCompany): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, company_uu_id=company_uu_id, token_dict=token_dict + ) + + +@company_route.patch( + path="/patch/{company_uu_id}", summary="Update Active/Delete/Confirm" +) +def company_company_patch(request: Request, company_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, company_uu_id=company_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/company/department/router.py b/ApiServices/EventService/routers/company/department/router.py new file mode 100644 index 0000000..92fd907 --- /dev/null +++ b/ApiServices/EventService/routers/company/department/router.py @@ -0,0 +1,47 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + DepartmentsPydantic, + PatchRecord, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +department_route = APIRouter(prefix="/department", tags=["Departments"]) +department_route.include_router(department_route, include_in_schema=True) + + +@department_route.post(path="/list", summary="List Active/Delete/Confirm Departments") +def company_department_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@department_route.post(path="/create", summary="Create Company with given auth levels") +def company_department_create(request: Request, data: DepartmentsPydantic): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@department_route.post( + path="/update/{company_uu_id}", summary="Update Company with given auth levels" +) +def company_department_update( + request: Request, company_uu_id: str, data: DepartmentsPydantic +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + company_uu_id=company_uu_id, data=data, token_dict=token_dict + ) + + +@department_route.patch( + path="/patch/{company_uu_id}", summary="Patch Company with given auth levels" +) +def company_department_patch(request: Request, company_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + company_uu_id=company_uu_id, data=data, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/company/duties/router.py b/ApiServices/EventService/routers/company/duties/router.py new file mode 100644 index 0000000..d4c8660 --- /dev/null +++ b/ApiServices/EventService/routers/company/duties/router.py @@ -0,0 +1,54 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertDuties, + UpdateDuties, + SelectDuties, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +duties_route = APIRouter(prefix="/duties", tags=["Duties"]) +duties_route.include_router(duties_route, include_in_schema=True) + + +@duties_route.post(path="/list", summary="List Active/Delete/Confirm Duties") +def company_duties_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@duties_route.post(path="/get_by_duty_uuid", summary="Get Single Duty by Duty UUID") +def company_duties_get_by_duty_uuid(request: Request, data: SelectDuties): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@duties_route.post(path="/create", summary="Create Duties with given auth levels") +def company_duties_create(request: Request, data: InsertDuties): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@duties_route.post( + path="/update/{duties_uu_id}", summary="Update Duties with given auth levels" +) +def company_duties_update(request: Request, duties_uu_id: str, data: UpdateDuties): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, duties_uu_id=duties_uu_id, token_dict=token_dict + ) + + +@duties_route.patch( + path="/patch/{duties_uu_id}", summary="Patch Duties with given auth levels" +) +def company_duties_patch(request: Request, duties_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, duties_uu_id=duties_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/company/duty/router.py b/ApiServices/EventService/routers/company/duty/router.py new file mode 100644 index 0000000..ce1a61b --- /dev/null +++ b/ApiServices/EventService/routers/company/duty/router.py @@ -0,0 +1,44 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertCompanyDuty, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +duty_route = APIRouter(prefix="/duty", tags=["Duty"]) +duty_route.include_router(duty_route, include_in_schema=True) + + +@duty_route.post(path="/list", summary="List Active/Delete/Confirm Duty") +def company_duty_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@duty_route.post(path="/create", summary="Create Company with given auth levels") +def company_duty_create(request: Request, data: InsertCompanyDuty): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@duty_route.post( + path="/update/{company_uu_id}", summary="Update Company with given auth levels" +) +def company_duty_update(request: Request, company_uu_id: str, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, company_uu_id=company_uu_id, token_dict=token_dict + ) + + +@duty_route.patch(path="/patch/{company_uu_id}", summary="Update Active/Delete/Confirm") +def company_duty_patch(request: Request, company_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, company_uu_id=company_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/company/employee/router.py b/ApiServices/EventService/routers/company/employee/router.py new file mode 100644 index 0000000..2d5c8a7 --- /dev/null +++ b/ApiServices/EventService/routers/company/employee/router.py @@ -0,0 +1,59 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertEmployees, + UnBindEmployees2People, + BindEmployees2People, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +employee_route = APIRouter(prefix="/employee", tags=["Employee"]) +employee_route.include_router(employee_route, include_in_schema=True) + + +@employee_route.post(path="/list", summary="List Active/Delete/Confirm Staff") +def company_employee_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@employee_route.post(path="/create", summary="Create Employee with given auth levels") +def company_employee_create(request: Request, data: InsertEmployees): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@employee_route.post( + path="/update/{employee_uu_id}", summary="Update Employee with given auth levels" +) +def company_employee_update(request: Request, employee_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, employee_uu_id=employee_uu_id, token_dict=token_dict + ) + + +@employee_route.patch( + path="/patch/{employee_uu_id}", summary="Update Active/Delete/Confirm" +) +def company_employee_patch(request: Request, employee_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, employee_uu_id=employee_uu_id, token_dict=token_dict + ) + + +@employee_route.post(path="/employ", summary="Employ Employee with given auth levels") +def company_employee_employ(request: Request, data: BindEmployees2People): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@employee_route.post(path="/fire", summary="UnEmploy Employee with given auth levels") +def company_employee_fire(request: Request, data: UnBindEmployees2People): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/company/staff/router.py b/ApiServices/EventService/routers/company/staff/router.py new file mode 100644 index 0000000..891df08 --- /dev/null +++ b/ApiServices/EventService/routers/company/staff/router.py @@ -0,0 +1,50 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertStaff, + SelectStaff, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +staff_route = APIRouter(prefix="/staff", tags=["Staff"]) +staff_route.include_router(staff_route, include_in_schema=True) + + +@staff_route.post(path="/list", summary="List Active/Delete/Confirm Staff") +def company_staff_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@staff_route.post(path="/get_by_duties_uu_id", summary="Get Staff by UUID") +def company_staff_get_by_uu_id(request: Request, data: SelectStaff): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@staff_route.post(path="/create", summary="Create Staff with given auth levels") +def company_staff_create(request: Request, data: InsertStaff): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@staff_route.post( + path="/update/{staff_uu_id}", summary="Update Staff with given auth levels" +) +def company_staff_update(request: Request, staff_uu_id: str, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, staff_uu_id=staff_uu_id, token_dict=token_dict + ) + + +@staff_route.patch(path="/patch/{staff_uu_id}", summary="Update Active/Delete/Confirm") +def company_staff_patch(request: Request, staff_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, staff_uu_id=staff_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/decision_book/decision_book/router.py b/ApiServices/EventService/routers/decision_book/decision_book/router.py new file mode 100644 index 0000000..fad3cc9 --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/decision_book/router.py @@ -0,0 +1,88 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertDecisionBook, + UpdateDecisionBook, + DecisionBookDecisionBookInvitations, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_route = APIRouter( + prefix="/build/decision_book", tags=["Decision Book"] +) +build_decision_book_route.include_router( + build_decision_book_route, include_in_schema=True +) + + +@build_decision_book_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book" +) +def build_decision_book_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/create", summary="Create Build Decision Book with given auth levels" +) +def build_decision_book_create(request: Request, data: InsertDecisionBook): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/approval", summary="Approve Build Decision Book with given auth levels" +) +def build_decision_book_approval(request: Request, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/update/{book_uu_id}", + summary="Update Build Decision Book with given auth levels", +) +def build_decision_book_update( + request: Request, book_uu_id: str, data: UpdateDecisionBook +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_route.patch( + path="/patch/{book_uu_id}", summary="Update Active/Delete/Confirm" +) +def build_decision_book_patch(request: Request, book_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/invite/list", summary="List Build Decision Book Invitations" +) +def build_decision_book_invite(request: Request, data: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=data, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/invite/create", summary="Create Build Decision Book Invitations" +) +def build_decision_book_invite_create( + request: Request, data: DecisionBookDecisionBookInvitations +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_route.post( + path="/invite/update", summary="Update Build Decision Book Invitations" +) +def build_decision_book_invite_update(request: Request, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/decision_book/decision_book_invitations/router.py b/ApiServices/EventService/routers/decision_book/decision_book_invitations/router.py new file mode 100644 index 0000000..4daf89f --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/decision_book_invitations/router.py @@ -0,0 +1,37 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + DecisionBookDecisionBookInvitationsAttend, + DecisionBookDecisionBookInvitationsAssign, + PatchRecord, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_invitations = APIRouter( + prefix="/build/decision_book/invitations", tags=["Decision Book Invitations"] +) +build_decision_book_invitations.include_router( + build_decision_book_invitations, include_in_schema=True +) + + +@build_decision_book_invitations.post( + path="/attend", summary="Decision Book Invitations Attend" +) +def build_decision_book_invitations_attend( + request: Request, data: DecisionBookDecisionBookInvitationsAttend +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_invitations.post( + path="/assign", summary="Decision Book Invitations Assign" +) +def build_decision_book_invitations_assign( + request: Request, data: DecisionBookDecisionBookInvitationsAssign +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/decision_book/decision_book_items/router.py b/ApiServices/EventService/routers/decision_book/decision_book_items/router.py new file mode 100644 index 0000000..7a2b0cb --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/decision_book_items/router.py @@ -0,0 +1,61 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildDecisionBookItems, + UpdateBuildDecisionBookItems, + ListDecisionBook, + PatchRecord, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_items_route = APIRouter( + prefix="/build/decision_book/items", tags=["Decision Book Items"] +) +build_decision_book_items_route.include_router( + build_decision_book_items_route, include_in_schema=True +) + + +@build_decision_book_items_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book Items" +) +def build_decision_book_items_list(request: Request, data: ListDecisionBook): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_items_route.post( + path="/create", summary="Create Build Items Decision Book with given auth levels" +) +def build_decision_book_items_create( + request: Request, data: InsertBuildDecisionBookItems +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_items_route.post( + path="/update/{book_uu_id}", + summary="Update Build Decision Book Items with given auth levels", +) +def build_decision_book_items_update( + request: Request, book_uu_id: str, data: UpdateBuildDecisionBookItems +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) + + +@build_decision_book_items_route.patch( + path="/patch/{book_uu_id}", summary="Update Active/Delete/Confirm" +) +def build_decision_book_items_patch( + request: Request, book_uu_id: str, data: PatchRecord +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/decision_book/decision_book_person/router.py b/ApiServices/EventService/routers/decision_book/decision_book_person/router.py new file mode 100644 index 0000000..6601db5 --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/decision_book_person/router.py @@ -0,0 +1,54 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertDecisionBook, + UpdateDecisionBook, + DecisionBookDecisionBookInvitationsAttend, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_people_route = APIRouter( + prefix="/build/decision_book/people", tags=["Decision Book People"] +) +build_decision_book_people_route.include_router( + build_decision_book_people_route, include_in_schema=True +) + + +@build_decision_book_people_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book People" +) +def build_decision_book_people_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_decision_book_people_route.post( + path="/add", + summary="Add people to Build Decision People Book with given auth levels", +) +def build_decision_book_people_add(request: Request, data: InsertDecisionBook): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_people_route.post( + path="/remove", + summary="Remove people from Build Decision Book People with given auth levels", +) +def build_decision_book_people_remove(request: Request, data: UpdateDecisionBook): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_people_route.post( + path="/attend", + summary="Attend to Build Decision Book Invitations with given auth levels", +) +def build_decision_book_invite_attend( + request: Request, data: DecisionBookDecisionBookInvitationsAttend +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/decision_book/project_decision_book/router.py b/ApiServices/EventService/routers/decision_book/project_decision_book/router.py new file mode 100644 index 0000000..18453c8 --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/project_decision_book/router.py @@ -0,0 +1,60 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildDecisionBookProjects, + UpdateBuildDecisionBookProjects, + ApprovalsBuildDecisionBookProjects, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_project_route = APIRouter( + prefix="/build/decision_book/project", tags=["Decision Book Project"] +) +build_decision_book_project_route.include_router( + build_decision_book_project_route, include_in_schema=True +) + + +@build_decision_book_project_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book People" +) +def build_decision_book_project_people_list( + request: Request, list_options: ListOptions +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_decision_book_project_route.post( + path="/create", + summary="Create Build Decision Book Project People with given auth levels", +) +def build_decision_book_project_people_create( + request: Request, data: InsertBuildDecisionBookProjects +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_project_route.post( + path="/update/{build_decision_book_project_id}", + summary="Add people to Build Decision People Book with given auth levels", +) +def build_decision_book_project_people_update( + request: Request, data: UpdateBuildDecisionBookProjects +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_project_route.post( + path="/approval", + summary="Approval people from Build Decision Book Project People with given auth levels", +) +def build_decision_book_project_invite_approval( + request: Request, data: ApprovalsBuildDecisionBookProjects +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/decision_book/project_decision_book_items/router.py b/ApiServices/EventService/routers/decision_book/project_decision_book_items/router.py new file mode 100644 index 0000000..29cd193 --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/project_decision_book_items/router.py @@ -0,0 +1,71 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildDecisionBookProjectItems, + UpdateBuildDecisionBookProjectItems, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_project_items_route = APIRouter( + prefix="/build/decision_book/project/items", + tags=["Decision Project Book Project Items"], +) +build_decision_book_project_items_route.include_router( + build_decision_book_project_items_route, include_in_schema=True +) + + +@build_decision_book_project_items_route.post( + path="/list", + summary="List Active/Delete/Confirm Decision Project Book Project Items", +) +def build_decision_book_project_people_items_list( + request: Request, list_options: ListOptions +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_decision_book_project_items_route.post( + path="/create", + summary="Create Build Decision Book Project People with given auth levels", +) +def build_decision_book_project_people_items_create( + request: Request, data: InsertBuildDecisionBookProjectItems +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_project_items_route.post( + path="/update/{build_decision_book_project_item_uu_id}", + summary="Add people to Decision Project Book Project Items with given auth levels", +) +def build_decision_book_project_people_items_update( + request: Request, + build_decision_book_project_item_uu_id: str, + data: UpdateBuildDecisionBookProjectItems, +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, + build_decision_book_project_item_uu_id=build_decision_book_project_item_uu_id, + token_dict=token_dict, + ) + + +@build_decision_book_project_items_route.post( + path="/patch/{build_decision_book_project_item_uu_id}", + summary="Patch people from Decision Project Book Project Items with given auth levels", +) +def build_decision_book_project_people_items_patch( + request: Request, build_decision_book_project_item_uu_id: str, data +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, + build_decision_book_project_item_uu_id=build_decision_book_project_item_uu_id, + token_dict=token_dict, + ) diff --git a/ApiServices/EventService/routers/decision_book/project_decision_book_person/router.py b/ApiServices/EventService/routers/decision_book/project_decision_book_person/router.py new file mode 100644 index 0000000..2f1e2d6 --- /dev/null +++ b/ApiServices/EventService/routers/decision_book/project_decision_book_person/router.py @@ -0,0 +1,71 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildDecisionBookProjectPerson, + UpdateBuildDecisionBookProjectPerson, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_decision_book_project_people_route = APIRouter( + prefix="/build/decision_book/project/people", tags=["Decision Book Project People"] +) +build_decision_book_project_people_route.include_router( + build_decision_book_project_people_route, include_in_schema=True +) + + +@build_decision_book_project_people_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book People" +) +def build_decision_book_project_people_list( + request: Request, list_options: ListOptions +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_decision_book_project_people_route.post( + path="/create", + summary="Create Build Decision Book Project People with given auth levels", +) +def build_decision_book_project_people_create( + request: Request, data: InsertBuildDecisionBookProjectPerson +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_decision_book_project_people_route.post( + path="/update/{build_decision_book_project_person_uu_id}", + summary="Add people to Build Decision People Book with given auth levels", +) +def build_decision_book_project_people_update( + request: Request, + build_decision_book_project_person_uu_id: str, + data: UpdateBuildDecisionBookProjectPerson, +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, + build_decision_book_project_person_uu_id=build_decision_book_project_person_uu_id, + token_dict=token_dict, + ) + + +@build_decision_book_project_people_route.post( + path="/patch/{build_decision_book_project_person_uu_id}", + summary="Patch people from Build Decision Book Project People with given auth levels", +) +def build_decision_book_project_people_patch( + request: Request, + build_decision_book_project_person_uu_id: str, + data: UpdateBuildDecisionBookProjectPerson, +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, + build_decision_book_project_person_uu_id=build_decision_book_project_person_uu_id, + token_dict=token_dict, + ) diff --git a/ApiServices/EventService/routers/events/events/bind_events_router.py b/ApiServices/EventService/routers/events/events/bind_events_router.py new file mode 100644 index 0000000..ba7426b --- /dev/null +++ b/ApiServices/EventService/routers/events/events/bind_events_router.py @@ -0,0 +1,35 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict +from api_validations.validations_request import ( + RegisterEvents2Employee, + RegisterEvents2Occupant, + PatchRecord, +) + + +bind_events_route = APIRouter(prefix="/bind/events", tags=["Binds"]) +bind_events_route.include_router(bind_events_route, include_in_schema=True) + + +@bind_events_route.post(path="/occupant", summary="Register Event to Occupant") +def bind_events_occupant(request: Request, data: RegisterEvents2Occupant): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@bind_events_route.post(path="/employee", summary="Register Event to Employee") +def bind_events_employee(request: Request, data: RegisterEvents2Employee): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@bind_events_route.patch( + path="/patch/{event_uu_id}", summary="Patch Bind Events with given auth levels" +) +def bind_events_patch(request: Request, event_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, event_uu_id=event_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/events/events/router.py b/ApiServices/EventService/routers/events/events/router.py new file mode 100644 index 0000000..7efd8e4 --- /dev/null +++ b/ApiServices/EventService/routers/events/events/router.py @@ -0,0 +1,18 @@ +from fastapi import Request +from fastapi.routing import APIRouter + +from api_validations.validations_request import ( + # CreateEvents, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +event_route = APIRouter(prefix="/event", tags=["Events"]) +event_route.include_router(event_route, include_in_schema=True) + + +@event_route.post(path="/list", summary="List Events") +def events_list(request: Request, data: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/events/modules/bind_events_router.py b/ApiServices/EventService/routers/events/modules/bind_events_router.py new file mode 100644 index 0000000..ea58aaa --- /dev/null +++ b/ApiServices/EventService/routers/events/modules/bind_events_router.py @@ -0,0 +1,35 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict +from api_validations.validations_request import ( + RegisterModules2Occupant, + RegisterModules2Employee, + PatchRecord, +) + + +bind_modules_route = APIRouter(prefix="/bind/modules", tags=["Binds"]) +bind_modules_route.include_router(bind_modules_route, include_in_schema=True) + + +@bind_modules_route.post(path="/occupant", summary="Register Event to Occupant") +def bind_events_occupant(request: Request, data: RegisterModules2Occupant): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@bind_modules_route.post(path="/employee", summary="Register Event to Employee") +def bind_events_employee(request: Request, data: RegisterModules2Employee): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@bind_modules_route.patch( + path="/patch/{event_uu_id}", summary="Patch Bind Events with given auth levels" +) +def bind_events_patch(request: Request, event_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, event_uu_id=event_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/events/modules/router.py b/ApiServices/EventService/routers/events/modules/router.py new file mode 100644 index 0000000..2497fac --- /dev/null +++ b/ApiServices/EventService/routers/events/modules/router.py @@ -0,0 +1,47 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + DepartmentsPydantic, + PatchRecord, + ListOptions, +) + + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +modules_route = APIRouter(prefix="/modules", tags=["Modules"]) +modules_route.include_router(modules_route, include_in_schema=True) + + +@modules_route.post(path="/list", summary="List Active/Delete/Confirm Modules") +def modules_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@modules_route.post(path="/create", summary="Create Modules with given auth levels") +def modules_create(request: Request, data: DepartmentsPydantic): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@modules_route.post( + path="/update/{module_uu_id}", summary="Update Modules with given auth levels" +) +def modules_update(request: Request, module_uu_id: str, data: DepartmentsPydantic): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, module_uu_id=module_uu_id, token_dict=token_dict + ) + + +@modules_route.patch( + path="/patch/{module_uu_id}", summary="Patch Modules with given auth levels" +) +def modules_patch(request: Request, module_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, module_uu_id=module_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/events/services/bind_services_router.py b/ApiServices/EventService/routers/events/services/bind_services_router.py new file mode 100644 index 0000000..dc119c3 --- /dev/null +++ b/ApiServices/EventService/routers/events/services/bind_services_router.py @@ -0,0 +1,24 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + RegisterServices2Employee, + RegisterServices2Occupant, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +bind_services_route = APIRouter(prefix="/bind/services", tags=["Binds"]) +bind_services_route.include_router(bind_services_route, include_in_schema=True) + + +@bind_services_route.post(path="/occupant", summary="Bind Services to Occupant") +def bind_services_occupant(request: Request, data: RegisterServices2Occupant): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@bind_services_route.post(path="/employee", summary="Bind Services to Employee") +def bind_services_employee(request: Request, data: RegisterServices2Employee): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) diff --git a/ApiServices/EventService/routers/events/services/router.py b/ApiServices/EventService/routers/events/services/router.py new file mode 100644 index 0000000..0e6cae5 --- /dev/null +++ b/ApiServices/EventService/routers/events/services/router.py @@ -0,0 +1,44 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +services_route = APIRouter(prefix="/services", tags=["Services"]) +services_route.include_router(services_route, include_in_schema=True) + + +@services_route.post(path="/list", summary="List Active/Delete/Confirm Modules") +def services_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@services_route.post(path="/create", summary="Create Modules with given auth levels") +def services_create(request: Request, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@services_route.post( + path="/update/{service_uu_id}", summary="Update Modules with given auth levels" +) +def services_update(request: Request, service_uu_id: str, data): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, service_uu_id=service_uu_id, token_dict=token_dict + ) + + +@services_route.patch( + path="/patch/{service_uu_id}", summary="Patch Modules with given auth levels" +) +def services_patch(request: Request, service_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, service_uu_id=service_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/people/router.py b/ApiServices/EventService/routers/people/router.py new file mode 100644 index 0000000..315d7ae --- /dev/null +++ b/ApiServices/EventService/routers/people/router.py @@ -0,0 +1,44 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertPerson, + UpdateUsers, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +people_router = APIRouter(prefix="/people", tags=["People"]) +people_router.include_router(people_router, include_in_schema=True) + + +@people_router.post(path="/list", summary="List Active/Delete/Confirm People") +def people_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@people_router.post(path="/create", summary="Create People with given auth levels") +def people_create(request: Request, data: InsertPerson): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@people_router.post( + path="/update/{user_uu_id}", summary="Update People with given auth levels" +) +def people_update(request: Request, user_uu_id: str, data: UpdateUsers): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, user_uu_id=user_uu_id, token_dict=token_dict + ) + + +@people_router.patch(path="/patch/{user_uu_id}", summary="Update Active/Delete/Confirm") +def people_patch(request: Request, user_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, user_uu_id=user_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/project_decision_book/project_decision_book/router.py b/ApiServices/EventService/routers/project_decision_book/project_decision_book/router.py new file mode 100644 index 0000000..40856aa --- /dev/null +++ b/ApiServices/EventService/routers/project_decision_book/project_decision_book/router.py @@ -0,0 +1,58 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertBuildDecisionBookProjects, + UpdateBuildDecisionBookProjects, + PatchRecord, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_project_decision_book_route = APIRouter( + prefix="/build/project/decision_book", tags=["Project Decision Book"] +) +build_project_decision_book_route.include_router( + build_project_decision_book_route, include_in_schema=True +) + + +@build_project_decision_book_route.post( + path="/list", summary="List Active/Delete/Confirm Project Build Decision Book" +) +def project_decision_book_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_project_decision_book_route.post( + path="/create", summary="Create Build Project Decision Book with given auth levels" +) +def project_decision_book_create( + request: Request, data: InsertBuildDecisionBookProjects +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_project_decision_book_route.post( + path="/update/{book_uu_id}", + summary="Update Project Build Decision Book with given auth levels", +) +def project_decision_book_update( + request: Request, book_uu_id: str, data: UpdateBuildDecisionBookProjects +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) + + +@build_project_decision_book_route.patch( + path="/patch/{book_uu_id}", summary="Update Active/Delete/Confirm" +) +def project_decision_book_patch(request: Request, book_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/project_decision_book/project_decision_book_person/router.py b/ApiServices/EventService/routers/project_decision_book/project_decision_book_person/router.py new file mode 100644 index 0000000..80c5c58 --- /dev/null +++ b/ApiServices/EventService/routers/project_decision_book/project_decision_book_person/router.py @@ -0,0 +1,58 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertDecisionBook, + UpdateDecisionBook, + PatchRecord, + ListOptions, +) +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + +build_project_decision_book_person_route = APIRouter( + prefix="/build/decision_book/person", tags=["Decision Book Person"] +) +build_project_decision_book_person_route.include_router( + build_project_decision_book_person_route, include_in_schema=True +) + + +@build_project_decision_book_person_route.post( + path="/list", summary="List Active/Delete/Confirm Build Decision Book" +) +def project_decision_book_person_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@build_project_decision_book_person_route.post( + path="/create", summary="Create Build Decision Book with given auth levels" +) +def project_decision_book_person_create(request: Request, data: InsertDecisionBook): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@build_project_decision_book_person_route.post( + path="/update/{book_uu_id}", + summary="Update Build Decision Book with given auth levels", +) +def project_decision_book_person_update( + request: Request, book_uu_id: str, data: UpdateDecisionBook +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) + + +@build_project_decision_book_person_route.patch( + path="/patch/{book_uu_id}", summary="Update Active/Delete/Confirm" +) +def project_decision_book_person_patch( + request: Request, book_uu_id: str, data: PatchRecord +): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, book_uu_id=book_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/rules/router.py b/ApiServices/EventService/routers/rules/router.py new file mode 100644 index 0000000..bc9c0b3 --- /dev/null +++ b/ApiServices/EventService/routers/rules/router.py @@ -0,0 +1,130 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_objects import OccupantTokenObject, EmployeeTokenObject +from api_validations.validations_request import ( + UpdateEndpointAccessList, + InsertEndpointAccess, + CheckEndpointAccess, +) + +from databases import ( + EndpointRestriction, + Event2Occupant, + Event2Employee, + Events, +) +from databases.sql_models.event.event import Services, Service2Events +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +endpoint_restriction_route = APIRouter(prefix="/access", tags=["Endpoint Access"]) +endpoint_restriction_route.include_router( + endpoint_restriction_route, include_in_schema=True +) + + +@endpoint_restriction_route.post( + path="/endpoint/restriction/create", + summary="Add extra restriction to endpoints list", +) +def endpoint_restriction_create(request: Request, data: InsertEndpointAccess): + token_dict = parse_token_object_to_dict(request=request) + return + + +@endpoint_restriction_route.post( + path="/endpoint/update", summary="Update extra restriction to endpoints list" +) +def endpoint_restriction_update(request: Request, data: UpdateEndpointAccessList): + token_dict = parse_token_object_to_dict(request=request) + return + + +@endpoint_restriction_route.post( + path="/endpoints/available", summary="List extra restriction to endpoints list" +) +def endpoint_restriction_list(request: Request): + token_dict, records = parse_token_object_to_dict(request=request), [] + if isinstance(token_dict, OccupantTokenObject): + occupant_events = Event2Occupant.get_event_id_by_build_living_space_id( + build_living_space_id=token_dict.selected_occupant.living_space_id + ) + events_list = Events.filter_all(Events.id.in_(occupant_events)).data + records = EndpointRestriction.filter_all( + EndpointRestriction.id.in_([event.endpoint_id for event in events_list]) + ).data + elif isinstance(token_dict, EmployeeTokenObject): + employee_events = Event2Employee.get_event_id_by_employee_id( + employee_id=token_dict.selected_company.employee_id + ) + events_list = Events.filter_all(Events.id.in_(employee_events)).data + records = EndpointRestriction.filter_all( + EndpointRestriction.id.in_([event.endpoint_id for event in events_list]) + ).data + return dict( + completed=True, + message="Available endpoints are listed successfully", + result=[str(record.endpoint_name) for record in records], + ) + + +@endpoint_restriction_route.post( + path="/endpoint/available", summary="Check extra restriction to endpoint available" +) +def endpoint_restriction_available(request: Request, data: CheckEndpointAccess): + token_dict, records = parse_token_object_to_dict(request=request), [] + endpoint = EndpointRestriction.filter_one( + EndpointRestriction.endpoint_name.ilike(f"%{data.endpoint}%"), + system=True, + ).data + if not endpoint: + EndpointRestriction.raise_http_exception( + status_code="HTTP_404_NOT_FOUND", + error_case="UNAUTHORIZED", + message="Only Occupant can see this data", + data={}, + ) + event = Events.filter_one(Events.endpoint_id == endpoint.id).data + service = Service2Events.filter_one(Service2Events.event_id == event.id).data + if isinstance(token_dict, OccupantTokenObject): + event_occupant = Event2Occupant.filter_one( + Event2Occupant.event_service_id == service.service_id, + Event2Occupant.build_living_space_id + == token_dict.selected_occupant.living_space_id, + ).data + if not event_occupant: + EndpointRestriction.raise_http_exception( + status_code="HTTP_404_NOT_FOUND", + error_case="UNAUTHORIZED", + message="This endpoint is not available for this occupant", + data={}, + ) + return dict( + completed=True, + message="Endpoint is available for this occupant", + ) + elif isinstance(token_dict, EmployeeTokenObject): + event_employee = Event2Employee.filter_one( + Event2Employee.event_service_id == service.service_id, + Event2Employee.employee_id == token_dict.selected_company.employee_id, + ).data + if not event_employee: + EndpointRestriction.raise_http_exception( + status_code="HTTP_404_NOT_FOUND", + error_case="UNAUTHORIZED", + message="This endpoint is not available for this employee", + data={}, + ) + return dict( + completed=True, + message="Endpoint is available for this occupant", + ) + + +@endpoint_restriction_route.patch( + path="/endpoint/bind/patch", summary="Patch extra restriction to endpoints list" +) +def endpoint_restriction_patch(request: Request): + token_dict = parse_token_object_to_dict(request=request) + return diff --git a/ApiServices/EventService/routers/users/router.py b/ApiServices/EventService/routers/users/router.py new file mode 100644 index 0000000..ee42f6c --- /dev/null +++ b/ApiServices/EventService/routers/users/router.py @@ -0,0 +1,45 @@ +from fastapi.routing import APIRouter +from fastapi.requests import Request + +from api_validations.validations_request import ( + InsertUsers, + UpdateUsers, + PatchRecord, + ListOptions, +) + +from ApiServices.api_handlers.auth_actions.token import parse_token_object_to_dict + + +user_route = APIRouter(prefix="/user", tags=["User"]) +user_route.include_router(user_route, include_in_schema=True) + + +@user_route.post(path="/list", summary="List Active/Delete/Confirm Users") +def user_list(request: Request, list_options: ListOptions): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(list_options=list_options, token_dict=token_dict) + + +@user_route.post(path="/create", summary="Create User with given auth levels") +def user_create(request: Request, data: InsertUsers): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event(data=data, token_dict=token_dict) + + +@user_route.post( + path="/update/{user_uu_id}", summary="Update User with given auth levels" +) +def user_update(request: Request, user_uu_id: str, data: UpdateUsers): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, user_uu_id=user_uu_id, token_dict=token_dict + ) + + +@user_route.patch(path="/patch/{user_uu_id}", summary="Update Active/Delete/Confirm") +def user_patch(request: Request, user_uu_id: str, data: PatchRecord): + token_dict = parse_token_object_to_dict(request=request) + return token_dict.available_event( + data=data, user_uu_id=user_uu_id, token_dict=token_dict + ) diff --git a/ApiServices/EventService/routers/validations/router.py b/ApiServices/EventService/routers/validations/router.py new file mode 100644 index 0000000..cee20e6 --- /dev/null +++ b/ApiServices/EventService/routers/validations/router.py @@ -0,0 +1,123 @@ +from fastapi import status +from fastapi.routing import APIRouter +from fastapi.requests import Request +from fastapi.exceptions import HTTPException +from api_validations.validations_request import ( + EndpointValidation, +) +from pydantic import BaseModel + + +validations_route = APIRouter(prefix="/validations", tags=["Validations"]) +validations_route.include_router(validations_route, include_in_schema=True) + + +class EndpointValidationResponse(BaseModel): + language: str + headers: dict + validation: dict + + +class ValidationParser: + + def __init__(self, active_validation): + self.annotations = ( + active_validation.__annotations__.items() if active_validation else None + ) + self.schema = {} + self.parse() + + def parse(self): + for key, value in self.annotations or {}: + field_type, required = "string", False + if str(value) == "" or str(value) == "typing.Optional[str]": + field_type = "string" + required = not str(value) == "typing.Optional[str]" + elif str(value) == "" or str(value) == "typing.Optional[int]": + field_type = "integer" + required = not str(value) == "typing.Optional[int]" + elif ( + str(value) == "" or str(value) == "typing.Optional[bool]" + ): + field_type = "boolean" + required = not str(value) == "typing.Optional[bool]" + elif ( + str(value) == "" + or str(value) == "typing.Optional[float]" + ): + field_type = "float" + required = not str(value) == "typing.Optional[bool]" + elif ( + str(value) == "" + or str(value) == "typing.Optional[datetime.datetime]" + ): + field_type = "datetime" + required = not str(value) == "typing.Optional[datetime.datetime]" + self.schema[key] = {"type": field_type, "required": required} + + +def retrieve_validation_from_class(selected_event, events): + event_function_class = getattr(selected_event, "function_class", None) + event_function_code = getattr(selected_event, "function_code", None) + function_class = getattr(events, event_function_class, None) + return function_class.__event_validation__.get(event_function_code, None) + + +@validations_route.post(path="/endpoint", summary="Retrieve validation of endpoint") +def user_list(request: Request, validation: EndpointValidation): + import api_events.events as events + from api_services.redis.functions import get_object_via_access_key + from databases import ( + EndpointRestriction, + Events, + ) + + valid_token = get_object_via_access_key(request=request) + if not valid_token: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=f"No valid token found in the request.", + ) + endpoint_active = EndpointRestriction.filter_one( + EndpointRestriction.endpoint_name.ilike(f"%{str(validation.endpoint)}%"), + system=True, + ).data + if not endpoint_active: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=f"This endpoint {str(validation.endpoint)} is not active for this user, please contact your responsible company for further information.", + ) + if valid_token.user_type == 1 and not valid_token.selected_company: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Selected company is not found in the token object.", + ) + elif valid_token.user_type == 2 and not valid_token.selected_occupant: + raise HTTPException( + status_code=status.HTTP_418_IM_A_TEAPOT, + detail="Selected occupant is not found in the token object.", + ) + selected_event = Events.filter_one( + Events.endpoint_id == endpoint_active.id, + Events.id.in_(valid_token.selected_company.reachable_event_list_id), + ).data + if not selected_event: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="This endpoint requires event validation. Please contact your responsible company to use this event.", + ) + active_validation = retrieve_validation_from_class(selected_event, events) + if not active_validation: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="No validation found for this endpoint.", + ) + headers = getattr( + active_validation, str(valid_token.lang).lower(), active_validation.tr + ) + validation_parse = ValidationParser(active_validation=active_validation) + return EndpointValidationResponse( + language=valid_token.lang, + headers=headers, + validation=validation_parse.schema, + ) diff --git a/ApiServices/ValidationService/Dockerfile b/ApiServices/ValidationService/Dockerfile index c3ee71f..1c90278 100644 --- a/ApiServices/ValidationService/Dockerfile +++ b/ApiServices/ValidationService/Dockerfile @@ -2,35 +2,35 @@ FROM python:3.12-slim-bookworm ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 +ENV PYTHONPATH=/service_app COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ +WORKDIR /service_app + # Create logs directory RUN mkdir -p /service_app/logs COPY ApiServices/ValidationService/pyproject.toml . -RUN uv venv -RUN uv pip install -r pyproject.toml +RUN uv venv .venv +RUN . .venv/bin/activate && uv pip install -r pyproject.toml -COPY ApiServices/ValidationService ./service_app -COPY ApiServices/api_handlers ./service_app/api_handlers +COPY ApiServices ./ApiServices +COPY databases ./databases +COPY api_services ./api_services +COPY api_objects ./api_objects +COPY api_configs ./api_configs +COPY api_events ./api_events +COPY api_library ./api_library +COPY api_validations ./api_validations -COPY databases ./service_app/databases -COPY api_services ./service_app/api_services -COPY api_objects ./service_app/api_objects -COPY api_configs ./service_app/api_configs -COPY api_events ./service_app/api_events -COPY api_library ./service_app/api_library -COPY api_validations ./service_app/api_validations +WORKDIR /service_app/ApiServices/ValidationService -WORKDIR /service_app +# Create startup script +RUN echo '#!/bin/bash\n\ +source /service_app/.venv/bin/activate\n\ +exec python app.py' > /service_app/start.sh && \ + chmod +x /service_app/start.sh -CMD ["uv", "run", "app.py"] - -# Old File -#FROM python:3.10 - -#RUN pip install --upgrade pip -#RUN pip install --no-cache-dir --upgrade -r requirements.txt -#CMD ["python", "-m", "app"] +CMD ["/service_app/start.sh"] \ No newline at end of file diff --git a/ApiServices/ValidationService/app.py b/ApiServices/ValidationService/app.py index 7c3e4f7..f60580f 100644 --- a/ApiServices/ValidationService/app.py +++ b/ApiServices/ValidationService/app.py @@ -2,7 +2,8 @@ import uvicorn import routers from fastapi.middleware.cors import CORSMiddleware -from fastapi.exceptions import HTTPException +from fastapi import Request, HTTPException, status +from fastapi.responses import JSONResponse from middlewares.token_middleware import AuthHeaderMiddleware from application.create_file import create_app @@ -23,8 +24,17 @@ app.add_middleware( ) app.add_middleware(AuthHeaderMiddleware) -app.add_exception_handler(HTTPException, ErrorHandlers.exception_handler_http) -app.add_exception_handler(Exception, ErrorHandlers.exception_handler_exception) +# Initialize error handlers +error_handlers = ErrorHandlers.create( + requests=Request, + exceptions=HTTPException, + response_model=JSONResponse, + status=status, +) + +# Register error handlers with bound methods +app.add_exception_handler(HTTPException, error_handlers.exception_handler_http) +app.add_exception_handler(Exception, error_handlers.exception_handler_exception) if __name__ == "__main__": uvicorn_config = { diff --git a/ApiServices/ValidationService/middlewares/token_middleware.py b/ApiServices/ValidationService/middlewares/token_middleware.py index 6e2b20d..dc26788 100644 --- a/ApiServices/ValidationService/middlewares/token_middleware.py +++ b/ApiServices/ValidationService/middlewares/token_middleware.py @@ -72,9 +72,9 @@ def check_if_path_secure(request, insecure_paths) -> bool: def check_if_token_is_not_valid(request, endpoint_name): - from api_services.redis.functions import get_object_via_access_key + from api_services.redis.functions import RedisActions - token_user = get_object_via_access_key(request) + token_user = RedisActions.get_object_via_access_key(request) if not token_user: return "Session geçerli değil. Lütfen tekrar giriş yapınız.", token_user diff --git a/ApiServices/ValidationService/pyproject.toml b/ApiServices/ValidationService/pyproject.toml index f105854..616477b 100644 --- a/ApiServices/ValidationService/pyproject.toml +++ b/ApiServices/ValidationService/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "wag-managment-api-service-version-3" +name = "wag-managment-api-service-version-3-validation-service" version = "0.1.0" description = "Wag Python API Service" readme = "README.md" diff --git a/ApiServices/api_handlers/auth_actions/auth.py b/ApiServices/api_handlers/auth_actions/auth.py index 6f80b7b..a9a0491 100644 --- a/ApiServices/api_handlers/auth_actions/auth.py +++ b/ApiServices/api_handlers/auth_actions/auth.py @@ -5,45 +5,18 @@ from api_objects import ( EmployeeTokenObject, UserType, ) -from api_services.redis.conn import redis_cli +from api_services import RedisActions from ApiServices.api_handlers.auth_actions.token import AccessObjectActions -def save_access_token_to_redis( - request, found_user, domain: str, access_token: str = None -): - from databases import ( - BuildLivingSpace, - BuildParts, - Companies, - Duties, - Departments, - Duty, - Employees, - Staff, - Addresses, - OccupantTypes, - ) +class AuthActions: - if not found_user: - raise HTTPException( - status_code=400, - detail=dict(message="User is not found."), - ) + @classmethod + def do_occupant_login_token( + cls, request, found_user, domain, access_token: str = None + ): + from databases import BuildLivingSpace, BuildParts, OccupantTypes - # Check user is already logged in or has a previous session - already_tokens = AccessObjectActions.get_object_via_user_uu_id( - user_id=found_user.uu_id - ) - for key, token_user in already_tokens.items(): - if token_user.get("domain", "") == domain: - redis_cli.delete(key) - - access_token = ( - found_user.generate_access_token() if not access_token else access_token - ) - # Prepare the user's details to save in Redis Session - if found_user.is_occupant: # Check if user is NOT an occupant living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_all( BuildLivingSpace.person_id == found_user.person_id ).data @@ -118,108 +91,155 @@ def save_access_token_to_redis( available_occupants=occupants_selection_dict, ) - list_employee = Employees.filter_all( - Employees.people_id == found_user.person_id, - ).data - companies_uu_id_list, companies_id_list, companies_list = [], [], [] - duty_uu_id_list, duty_id_list = [], [] - for employee in list_employee: - staff = Staff.filter_one(Staff.id == employee.staff_id).data - if duties := Duties.filter_one(Duties.id == staff.duties_id).data: - if duty_found := Duty.filter_by_one(id=duties.duties_id).data: - duty_uu_id_list.append(str(duty_found.uu_id)) - duty_id_list.append(duty_found.id) + @classmethod + def do_employee_login_token( + cls, request, found_user, domain, access_token: str = None + ): + from databases import ( + Companies, + Duties, + Departments, + Duty, + Employees, + Staff, + Addresses, + ) - department = Departments.filter_one( - Departments.id == duties.department_id, + list_employee = Employees.filter_all( + Employees.people_id == found_user.person_id, ).data - if company := Companies.filter_one( - Companies.id == department.company_id, - ).data: - companies_uu_id_list.append(str(company.uu_id)) - companies_id_list.append(company.id) - company_address = Addresses.filter_by_one( - id=company.official_address_id + companies_uu_id_list, companies_id_list, companies_list = [], [], [] + duty_uu_id_list, duty_id_list = [], [] + for employee in list_employee: + staff = Staff.filter_one(Staff.id == employee.staff_id).data + if duties := Duties.filter_one(Duties.id == staff.duties_id).data: + if duty_found := Duty.filter_by_one(id=duties.duties_id).data: + duty_uu_id_list.append(str(duty_found.uu_id)) + duty_id_list.append(duty_found.id) + + department = Departments.filter_one( + Departments.id == duties.department_id, ).data - companies_list.append( - dict( - uu_id=str(company.uu_id), - public_name=company.public_name, - company_type=company.company_type, - company_address=company_address, + if company := Companies.filter_one( + Companies.id == department.company_id, + ).data: + companies_uu_id_list.append(str(company.uu_id)) + companies_id_list.append(company.id) + company_address = Addresses.filter_by_one( + id=company.official_address_id + ).data + companies_list.append( + dict( + uu_id=str(company.uu_id), + public_name=company.public_name, + company_type=company.company_type, + company_address=company_address, + ) ) + AccessObjectActions.save_object_to_redis( + access_token=access_token, + model_object=EmployeeTokenObject( + domain=domain, + user_type=UserType.employee.value, + user_uu_id=str(found_user.uu_id), + credentials=found_user.credentials(), + user_id=found_user.id, + person_id=found_user.person_id, + person_uu_id=str(found_user.person.uu_id), + request=dict(request.headers), + companies_uu_id_list=companies_uu_id_list, + companies_id_list=companies_id_list, + duty_uu_id_list=duty_uu_id_list, + duty_id_list=duty_id_list, + ), + ) + return dict( + user_type=UserType.employee.name, + companies_list=companies_list, + ) + + @classmethod + def check_user_and_delete_tokens_from_redis(cls, domain: str, found_user=None): + # Check user is already logged in or has a previous session + if not found_user: + raise HTTPException( + status_code=400, + detail=dict(message="User is not found."), ) - AccessObjectActions.save_object_to_redis( - access_token=access_token, - model_object=EmployeeTokenObject( - domain=domain, - user_type=UserType.employee.value, - user_uu_id=str(found_user.uu_id), - credentials=found_user.credentials(), - user_id=found_user.id, - person_id=found_user.person_id, - person_uu_id=str(found_user.person.uu_id), - request=dict(request.headers), - companies_uu_id_list=companies_uu_id_list, - companies_id_list=companies_id_list, - duty_uu_id_list=duty_uu_id_list, - duty_id_list=duty_id_list, - ), - ) - return dict( - user_type=UserType.employee.name, - companies_list=companies_list, - ) + already_tokens = AccessObjectActions.get_object_via_user_uu_id( + user_id=found_user.uu_id + ) + for key, token_user in already_tokens.items(): + if token_user.get("domain", "") == domain: + RedisActions.delete(key) + @classmethod + def save_access_token_to_redis( + cls, request, found_user, domain: str, access_token: str = None + ): + from databases.sql_models.identity.identity import Users -def update_selected_to_redis(request, add_payload): - already_tokens = AccessObjectActions.get_object_via_access_key(request=request) - if not hasattr(request, "headers"): + found_user: Users = found_user + cls.check_user_and_delete_tokens_from_redis(domain, found_user) + access_token = ( + found_user.generate_access_token() if not access_token else access_token + ) + # Prepare the user's details to save in Redis Session + if found_user.is_occupant: # Check if user is NOT an occupant + return cls.do_occupant_login_token( + request, found_user, domain, access_token + ) + return cls.do_employee_login_token(request, found_user, domain, access_token) + + @classmethod + def update_selected_to_redis(cls, request, add_payload): + already_tokens = AccessObjectActions.get_object_via_access_key(request=request) + if not hasattr(request, "headers"): + raise HTTPException( + status_code=401, + detail=dict( + message="Headers are not found in request. Invalid request object." + ), + ) + access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) + if already_tokens.user_type == UserType.occupant.value: + already_tokens.selected_occupant = add_payload.model_dump() + return AccessObjectActions.save_object_to_redis( + access_token=access_token, + model_object=OccupantTokenObject(**already_tokens.model_dump()), + ) + elif already_tokens.user_type == UserType.employee.value: + already_tokens.selected_company = add_payload.model_dump() + return AccessObjectActions.save_object_to_redis( + access_token=access_token, + model_object=EmployeeTokenObject(**already_tokens.model_dump()), + ) raise HTTPException( status_code=401, detail=dict( - message="Headers are not found in request. Invalid request object." + message="User type is not found in the token object. Please reach to your administrator." ), ) - access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) - if already_tokens.user_type == UserType.occupant.value: - already_tokens.selected_occupant = add_payload.model_dump() + + @classmethod + def update_access_token_to_redis(cls, request, add_payload): + already_tokens = AccessObjectActions.get_object_via_access_key(request=request) + if not hasattr(request, "headers"): + raise HTTPException( + status_code=401, + detail=dict( + message="Headers are not found in request. Invalid request object." + ), + ) + payload = {**add_payload, **already_tokens} + access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) + if payload.get("user_type") == str(UserType.occupant.value): + + return AccessObjectActions.save_object_to_redis( + access_token=access_token, + model_object=OccupantTokenObject(**payload), + ) return AccessObjectActions.save_object_to_redis( access_token=access_token, - model_object=OccupantTokenObject(**already_tokens.model_dump()), + model_object=EmployeeTokenObject(**payload), ) - elif already_tokens.user_type == UserType.employee.value: - already_tokens.selected_company = add_payload.model_dump() - return AccessObjectActions.save_object_to_redis( - access_token=access_token, - model_object=EmployeeTokenObject(**already_tokens.model_dump()), - ) - raise HTTPException( - status_code=401, - detail=dict( - message="User type is not found in the token object. Please reach to your administrator." - ), - ) - - -def update_access_token_to_redis(request, add_payload): - already_tokens = AccessObjectActions.get_object_via_access_key(request=request) - if not hasattr(request, "headers"): - raise HTTPException( - status_code=401, - detail=dict( - message="Headers are not found in request. Invalid request object." - ), - ) - payload = {**add_payload, **already_tokens} - access_token = request.headers.get(Auth.ACCESS_TOKEN_TAG) - if payload.get("user_type") == str(UserType.occupant.value): - - return AccessObjectActions.save_object_to_redis( - access_token=access_token, - model_object=OccupantTokenObject(**payload), - ) - return AccessObjectActions.save_object_to_redis( - access_token=access_token, - model_object=EmployeeTokenObject(**payload), - ) diff --git a/README.md b/README.md index 48397fc..0ff7c52 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,6 @@ On Linux > docker compose -f ./api-docker-compose.yml up --build -d + +pytest -v testers/authentication/test_login.py + diff --git a/api-docker-compose.yml b/api-docker-compose.yml index e62caec..52fd34e 100644 --- a/api-docker-compose.yml +++ b/api-docker-compose.yml @@ -1,8 +1,7 @@ services: - wag_management_auth_service: container_name: wag_management_auth_service -# restart: on-failure + # restart: on-failure build: context: . dockerfile: ApiServices/AuthService/Dockerfile @@ -14,39 +13,39 @@ services: - auth_venv:/service_app/.venv - auth_logs:/service_app/logs - wag_management_event_service: - container_name: wag_management_event_service -# restart: on-failure - build: - context: . - dockerfile: ApiServices/EventService/Dockerfile - ports: - - "1112:41575" - environment: - - PYTHONPATH=/service_app - volumes: - - event_venv:/service_app/.venv - - event_logs:/service_app/logs + # wag_management_event_service: + # container_name: wag_management_event_service + # # restart: on-failure + # build: + # context: . + # dockerfile: ApiServices/EventService/Dockerfile + # ports: + # - "1112:41575" + # environment: + # - PYTHONPATH=/service_app + # volumes: + # - event_venv:/service_app/.venv + # - event_logs:/service_app/logs - wag_management_validation_service: - container_name: wag_management_validation_service -# restart: on-failure - build: - context: . - dockerfile: ApiServices/ValidationService/Dockerfile - ports: - - "1113:41575" - environment: - - PYTHONPATH=/service_app - volumes: - - validation_venv:/service_app/.venv - - validation_logs:/service_app/logs + # wag_management_validation_service: + # container_name: wag_management_validation_service + # # restart: on-failure + # build: + # context: . + # dockerfile: ApiServices/ValidationService/Dockerfile + # ports: + # - "1113:41575" + # environment: + # - PYTHONPATH=/service_app + # volumes: + # - validation_venv:/service_app/.venv + # - validation_logs:/service_app/logs -# wag_management_init_service: -# container_name: wag_management_init_service -# build: -# context: . -# dockerfile: service_app_init/Dockerfile + # wag_management_init_service: + # container_name: wag_management_init_service + # build: + # context: . + # dockerfile: service_app_init/Dockerfile volumes: auth_venv: diff --git a/api_events/events/application/authentication.py b/api_events/events/application/authentication.py index b34d242..19f2e36 100644 --- a/api_events/events/application/authentication.py +++ b/api_events/events/application/authentication.py @@ -1,14 +1,39 @@ -import typing +import json from typing import Union from fastapi import status from fastapi.requests import Request from fastapi.exceptions import HTTPException from api_objects import OccupantTokenObject, EmployeeTokenObject +from api_objects.auth.token_objects import CompanyToken, OccupantToken +from api_services.templates.password_templates import ( + password_is_changed_template, + change_your_password_template, +) from api_services.token_service import TokenService from api_services.redis.functions import RedisActions from api_library.response_handlers import ResponseHandler -from api_library.logger import user_logger +from api_library.date_time_actions.date_functions import system_arrow + +# from api_library.user_logger import UserLogger + +from api_validations.validations_request import ( + Login, + Logout, + ChangePassword, + EmployeeSelection, + OccupantSelection, + CreatePassword, + Forgot, + # ResetPassword, + # RefreshToken, +) +from api_validations.validations_response import ( + AuthenticationLoginResponse, + AuthenticationRefreshResponse, + AuthenticationUserInfoResponse, +) +from ApiServices.api_handlers.auth_actions.auth import AuthActions from api_configs import Auth, ApiStatic from api_events.events.abstract_class import MethodToEvent, ActionsSchema @@ -32,27 +57,6 @@ from databases import ( from api_services import ( send_email, - save_access_token_to_redis, - update_selected_to_redis, - password_is_changed_template, - change_your_password_template, -) - -from api_validations.validations_request import ( - Login, - Logout, - ChangePassword, - Remember, - Forgot, - CreatePassword, - OccupantSelection, - EmployeeSelection, -) - -from api_validations.validations_response import ( - AuthenticationLoginResponse, - AuthenticationRefreshResponse, - AuthenticationUserInfoResponse, ) @@ -73,22 +77,20 @@ class AuthenticationLoginEventMethods(MethodToEvent): try: access_dict = Users.login_user_with_credentials(data=data, request=request) found_user = access_dict.get("user") - if not found_user: - user_logger.log_login_attempt( - request, - None, - data.domain, - data.access_key, - success=False, - error="Invalid credentials", - ) + # UserLogger.log_login_attempt( + # request, + # None, + # data.domain, + # data.access_key, + # success=False, + # error="Invalid credentials", + # ) return ResponseHandler.unauthorized("Invalid credentials") - user_logger.log_login_attempt( - request, found_user.id, data.domain, data.access_key, success=True - ) - + # UserLogger.log_login_attempt( + # request, found_user.id, data.domain, data.access_key, success=True + # ) response_data = { "access_token": access_dict.get("access_token"), "refresh_token": access_dict.get("refresher_token"), @@ -98,12 +100,11 @@ class AuthenticationLoginEventMethods(MethodToEvent): return ResponseHandler.success( message="User logged in successfully", data=response_data, - response_model=AuthenticationLoginResponse, ) except Exception as e: - user_logger.log_login_attempt( - request, None, data.domain, data.access_key, success=False, error=str(e) - ) + # UserLogger.log_login_attempt( + # request, None, data.domain, data.access_key, success=False, error=str(e) + # ) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) @@ -198,7 +199,7 @@ class AuthenticationSelectEventMethods(MethodToEvent): ) # Update Redis - update_selected_to_redis(request=request, add_payload=company_token) + AuthActions.update_selected_to_redis(request=request, add_payload=company_token) return ResponseHandler.success("Company selected successfully") @classmethod @@ -265,7 +266,9 @@ class AuthenticationSelectEventMethods(MethodToEvent): ) # Update Redis - update_selected_to_redis(request=request, add_payload=occupant_token) + AuthActions.update_selected_to_redis( + request=request, add_payload=occupant_token + ) return ResponseHandler.success("Occupant selected successfully") @classmethod @@ -305,8 +308,8 @@ class AuthenticationCheckTokenEventMethods(MethodToEvent): @classmethod def authentication_check_token_is_valid(cls, request: Request): try: - TokenService.validate_token(request) - return ResponseHandler.success("Access Token is valid") + if RedisActions.get_object_via_access_key(request=request): + return ResponseHandler.success("Access Token is valid") except HTTPException: return ResponseHandler.unauthorized("Access Token is NOT valid") @@ -338,16 +341,11 @@ class AuthenticationRefreshEventMethods(MethodToEvent): found_user = Users.filter_one(Users.uu_id == token_dict.user_uu_id).data if not found_user: return ResponseHandler.not_found("User not found") - user_token = UsersTokens.filter_one( UsersTokens.domain == found_user.domain_name, UsersTokens.user_id == found_user.id, UsersTokens.token_type == "RememberMe", ).data - - # Update user metadata - TokenService.update_user_metadata(found_user, request) - response_data = { "access_token": access_token, "refresh_token": getattr(user_token, "token", None), @@ -356,7 +354,6 @@ class AuthenticationRefreshEventMethods(MethodToEvent): return ResponseHandler.success( "User info refreshed successfully", data=response_data, - response_model=AuthenticationRefreshResponse, ) except Exception as e: return ResponseHandler.error(str(e)) @@ -392,29 +389,29 @@ class AuthenticationChangePasswordEventMethods(MethodToEvent): return ResponseHandler.not_found("User not found") if not found_user.check_password(data.old_password): - user_logger.log_password_change( - request, - found_user.id, - "change", - success=False, - error="Invalid old password", - ) + # UserLogger.log_password_change( + # request, + # found_user.id, + # "change", + # success=False, + # error="Invalid old password", + # ) return ResponseHandler.unauthorized("Old password is incorrect") found_user.set_password(data.new_password) - user_logger.log_password_change( - request, found_user.id, "change", success=True - ) + # UserLogger.log_password_change( + # request, found_user.id, "change", success=True + # ) return ResponseHandler.success("Password changed successfully") except Exception as e: - user_logger.log_password_change( - request, - found_user.id if found_user else None, - "change", - success=False, - error=str(e), - ) + # UserLogger.log_password_change( + # request, + # found_user.id if found_user else None, + # "change", + # success=False, + # error=str(e), + # ) return ResponseHandler.error(str(e)) @@ -484,7 +481,7 @@ class AuthenticationDisconnectUserEventMethods(MethodToEvent): user_id=str(found_user.uu_id) ): for key, token_user in already_tokens.items(): - RedisActions.delete_key(key) + RedisActions.delete(key) selected_user = Users.filter_one( Users.uu_id == token_user.get("uu_id"), ).data @@ -518,9 +515,9 @@ class AuthenticationLogoutEventMethods(MethodToEvent): token_user = None if already_tokens := RedisActions.get_object_via_access_key(request=request): for key in already_tokens: - token_user = json.loads(RedisActions.get_key(key) or {}) + token_user = RedisActions.get_json(key) if token_user.get("domain") == data.domain: - RedisActions.delete_key(key) + RedisActions.delete(key) selected_user = Users.filter_one( Users.uu_id == token_user.get("uu_id"), ).data @@ -548,7 +545,11 @@ class AuthenticationRefreshTokenEventMethods(MethodToEvent): @classmethod def authentication_refresher_token( - cls, request: Request, data: Remember, token_dict: dict = None + # cls, request: Request, data: RefreshToken, token_dict: dict = None + cls, + request: Request, + data, + token_dict: dict = None, ): token_refresher = UsersTokens.filter_by_one( token=data.refresh_token, @@ -561,7 +562,7 @@ class AuthenticationRefreshTokenEventMethods(MethodToEvent): Users.id == token_refresher.user_id, ).data: found_user: Users = found_user - access_key = save_access_token_to_redis( + access_key = AuthActions.save_access_token_to_redis( request=request, found_user=found_user, domain=data.domain ) found_user.last_agent = request.headers.get("User-Agent", None) @@ -577,7 +578,6 @@ class AuthenticationRefreshTokenEventMethods(MethodToEvent): return ResponseHandler.success( "User is logged in successfully via refresher token", data=response_data, - response_model=AuthenticationRefreshResponse, ) return ResponseHandler.not_found("Invalid data") @@ -604,7 +604,7 @@ class AuthenticationForgotPasswordEventMethods(MethodToEvent): found_user: Users = Users.check_user_exits( access_key=data.access_key, domain=data.domain ) - forgot_key = save_access_token_to_redis( + forgot_key = AuthActions.save_access_token_to_redis( request=request, found_user=found_user, domain=data.domain ) forgot_link = ApiStatic.forgot_link(forgot_key=forgot_key) @@ -708,7 +708,6 @@ class AuthenticationDownloadAvatarEventMethods(MethodToEvent): return ResponseHandler.success( "Avatar and profile is shared via user credentials", data=user_info, - response_model=AuthenticationUserInfoResponse, ) return ResponseHandler.not_found("Invalid data") diff --git a/api_services/__init__.py b/api_services/__init__.py index 5dce80c..e577071 100644 --- a/api_services/__init__.py +++ b/api_services/__init__.py @@ -1,11 +1,13 @@ from .email.service import send_email -from .redis.old_functions import ( - save_object_to_redis as save_access_token_to_redis, -) from .redis.functions import RedisActions from .templates.password_templates import ( password_is_changed_template, change_your_password_template, ) -update_selected_to_redis = RedisActions.set_json +_all__ = [ + "send_email", + "RedisActions", + "password_is_changed_template", + "change_your_password_template", +] diff --git a/api_services/redis/functions.py b/api_services/redis/functions.py index 751a075..dbb3e51 100644 --- a/api_services/redis/functions.py +++ b/api_services/redis/functions.py @@ -1,7 +1,8 @@ import json import typing + from api_services.redis.conn import redis_cli -from api_library.date_time_actions.date_functions import system_arrow +from api_library.date_time_actions.date_functions import system_arrow, DateTimeLocal from api_objects import OccupantTokenObject, EmployeeTokenObject diff --git a/databases/extensions/auth.py b/databases/extensions/auth.py index dd4b691..9970b8b 100644 --- a/databases/extensions/auth.py +++ b/databases/extensions/auth.py @@ -1,40 +1,28 @@ -import uuid -import secrets import hashlib +import uuid import requests -from sqlalchemy import or_ from datetime import timedelta from fastapi.exceptions import HTTPException -from fastapi import status - from databases.no_sql_models.validations import ( PasswordHistoryViaUser, AccessHistoryViaUser, ) - from api_library.date_time_actions.date_functions import system_arrow, client_arrow from api_configs import ApiStatic, Auth class PasswordModule: + @staticmethod + def generate_token(length=32): + return uuid.uuid4().__str__()[:length] + + @staticmethod + def create_hashed_password(domain: str, id_: str, password: str): + return hashlib.sha256(f"{domain}:{id_}:{password}".encode("utf-8")).hexdigest() @classmethod - def generate_token(cls, length): - return secrets.token_urlsafe(length) - - @classmethod - def create_hashed_password(cls, domain, id_, password): - salted_password = f"{domain}-{id_}-{password}" - return hashlib.sha256(salted_password.encode()).hexdigest() - - @classmethod - def check_hashed_password(cls, domain, id_, password, password_hashed): - if not password_hashed: - raise HTTPException( - status_code=401, - detail="Password is not changed yet user has no password.", - ) + def check_password(cls, domain, id_, password, password_hashed): return cls.create_hashed_password(domain, id_, password) == password_hashed @@ -43,8 +31,10 @@ class AuthModule(PasswordModule): @classmethod def check_user_exits(cls, access_key, domain): from databases import Users + from sqlalchemy import or_ + from fastapi import status - found_user = Users.query.filter( + found_user: Users = Users.query.filter( or_( Users.email == str(access_key).lower(), Users.phone_number == str(access_key).replace(" ", ""), @@ -70,9 +60,7 @@ class AuthModule(PasswordModule): return self.generate_token(Auth.ACCESS_TOKEN_LENGTH) def remove_refresher_token(self, domain, disconnect: bool = False): - from databases import ( - UsersTokens, - ) + from databases import UsersTokens if disconnect: registered_tokens = UsersTokens.filter_all( @@ -87,20 +75,6 @@ class AuthModule(PasswordModule): registered_tokens.query.delete() UsersTokens.save() - def check_password(self, password): - main_domain = self.get_main_domain_and_other_domains(get_main_domain=True) - if check_password := self.check_hashed_password( - domain=main_domain, - id_=str(self.uu_id), - password_hashed=self.hash_password, - password=password, - ): - return check_password - raise HTTPException( - status_code=401, - detail="Password is not correct.", - ) - def check_password_is_different(self, password): main_domain = self.get_main_domain_and_other_domains(get_main_domain=True) if self.hash_password == self.create_hashed_password( @@ -113,7 +87,9 @@ class AuthModule(PasswordModule): @staticmethod def create_password(found_user, password, password_token=None): - from databases import MongoQueryIdentity + from databases import MongoQueryIdentity, Users + + found_user: Users = found_user if found_user.password_token: replace_day = 0 @@ -135,10 +111,9 @@ class AuthModule(PasswordModule): detail="Password token is not valid. Please request a new password token.", ) query_engine = MongoQueryIdentity(company_uuid=found_user.related_company) - domain_via_user = query_engine.get_domain_via_user( user_uu_id=str(found_user.uu_id) - )["main_domain"] + )[0]["main_domain"] new_password_dict = { "password": found_user.create_hashed_password( domain=domain_via_user, id_=str(found_user.uu_id), password=password @@ -208,155 +183,133 @@ class AuthModule(PasswordModule): class UserLoginModule(AuthModule): - @classmethod - def login_user_with_credentials(cls, data, request): - from api_services.redis.functions import RedisActions - - from databases import ( - Users, - People, - MongoQueryIdentity, - ) + def set_login_details_to_mongo_database( + cls, found_user, headers_request, access_token, record_id + ): + from databases.no_sql_models.identity import MongoQueryIdentity - found_user: Users = Users.check_user_exits( - access_key=data.access_key, domain=data.domain + agent = headers_request.get("evyos-user-agent", "") + platform = headers_request.get("evyos-platform", "") + ext_ip = headers_request.get("evyos-ip-ext") + address = requests.get(f"http://ip-api.com/json/{ext_ip}").json() + address_package = { + "city": address["city"], + "zip": address["zip"], + "country": address["country"], + "countryCode": address["countryCode"], + "region": address["region"], + "regionName": address["regionName"], + } + mongo_db = MongoQueryIdentity( + company_uuid=found_user.related_company, + storage_reasoning="AccessHistory", ) - access_token = found_user.generate_access_token() query_engine = MongoQueryIdentity(company_uuid=found_user.related_company) - if found_user.check_password(password=data.password): - access_object_to_redis = RedisActions.save_access_token_to_redis( - request=request, - found_user=found_user, - domain=data.domain, - access_token=access_token, - ) - refresher_token = found_user.generate_refresher_token( - domain=data.domain, remember_me=data.remember_me - ) - headers_request = request.headers - headers_request = dict(headers_request) - headers_request["evyos-user-agent"] = headers_request.get("user-agent") - headers_request["evyos-platform"] = headers_request.get("user-agent") - headers_request["evyos-ip-ext"] = "94.54.68.158" - # found_user.last_agent = headers_request.get("evyos-user-agent", None) - # found_user.last_platform = headers_request.get("evyos-platform", None) - # found_user.last_remote_addr = headers_request.get("evyos-ip-ext", None) - # found_user.last_seen = str(system_arrow.now()) - if ext_ip := headers_request.get("evyos-ip-ext"): - agent = headers_request.get("evyos-user-agent", "") - platform = headers_request.get("evyos-platform", "") - address = requests.get(f"http://ip-api.com/json/{ext_ip}").json() - address_package = { - "city": address["city"], - "zip": address["zip"], - "country": address["country"], - "countryCode": address["countryCode"], - "region": address["region"], - "regionName": address["regionName"], + filter_query = { + "agent": agent, + "platform": platform, + "address": address_package, + "user_id": found_user.id, + } + already_exits = mongo_db.mongo_engine.filter_by(filter_query) or None + no_address_validates = mongo_db.mongo_engine.get_all()[0] == 0 + access_via_user = query_engine.update_access_history_via_user( + AccessHistoryViaUser( + **{ + "user_uu_id": found_user.uu_id.__str__(), + "access_history": { + "record_id": record_id, + "agent": agent, + "platform": platform, + "address": address_package, + "ip": ext_ip, + "access_token": access_token, + "created_at": system_arrow.now().timestamp(), + }, } - mongo_db = MongoQueryIdentity( - company_uuid=str(found_user.related_company).replace(" ", ""), - storage_reasoning="AccessHistory", - ) - filter_query = { + ) + ) + if already_exits: + update_mongo = mongo_db.mongo_engine.table.update_one( + filter=filter_query, + update={ + "$set": { + "ip": ext_ip, + "access_token": access_token, + "created_at": system_arrow.now().timestamp(), + } + }, + ) + print("update_mongo", update_mongo) + else: + insert_mongo = mongo_db.mongo_engine.insert( + payload={ + "user_id": found_user.id, + "record_id": record_id, "agent": agent, "platform": platform, "address": address_package, - "user_id": found_user.id, + "ip": ext_ip, + "access_token": access_token, + "created_at": system_arrow.now().timestamp(), + "is_confirmed": True if no_address_validates else False, + "is_first": True if no_address_validates else False, } - already_exits = mongo_db.mongo_engine.filter_by(filter_query) or None - no_address_validates = mongo_db.mongo_engine.get_all()[0] == 0 + ) + print("insert_mongo", insert_mongo) + + @classmethod + def login_user_with_credentials(cls, data, request): + from databases.sql_models.identity.identity import Users + from ApiServices.api_handlers.auth_actions.auth import AuthActions + + found_user = Users.check_user_exits( + access_key=data.access_key, domain=data.domain + ) + if not found_user: + raise HTTPException( + status_code=401, + detail="Login is not successful. Please check your credentials.", + ) + + if len(found_user.hash_password) < 5: + raise HTTPException( + status_code=401, + detail="This user has not set up password. Please contact support.", + ) + + if found_user.check_password( + domain=data.domain, + id_=found_user.uu_id, + password_hashed=found_user.hash_password, + password=data.password, + ): + access_object_to_redis = AuthActions.save_access_token_to_redis( + request=request, + found_user=found_user, + domain=data.domain, + # remember_me=data.remember_me, + ) + print("access_object_to_redis", access_object_to_redis) + access_token = access_object_to_redis.get("access_token") + headers_request = dict(request.headers) + headers_request["evyos-user-agent"] = headers_request.get("user-agent") + headers_request["evyos-platform"] = headers_request.get("user-agent") + headers_request["evyos-ip-ext"] = "94.54.68.158" + if headers_request.get("evyos-ip-ext"): record_id = uuid.uuid4().__str__() - notice_link = ApiStatic.blacklist_login(record_id=record_id) - found_people = People.filter_one(People.id == found_user.person_id).data - access_via_user = query_engine.update_access_history_via_user( - AccessHistoryViaUser( - **{ - "user_uu_id": found_user.uu_id.__str__(), - "access_history": { - "record_id": record_id, - "agent": agent, - "platform": platform, - "address": address_package, - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - # "is_confirmed": True if no_address_validates else False, - # "is_first": True if no_address_validates else False, - }, - } - ) + cls.set_login_details_to_mongo_database( + found_user=found_user, + headers_request=headers_request, + access_token=access_token, + record_id=record_id, ) - if already_exits: - update_mongo = mongo_db.mongo_engine.table.update_one( - filter=filter_query, - update={ - "$set": { - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - } - }, - ) - else: - mongo_db.mongo_engine.insert( - payload={ - "user_id": found_user.id, - "record_id": record_id, - "agent": agent, - "platform": platform, - "address": address_package, - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - "is_confirmed": True if no_address_validates else False, - "is_first": True if no_address_validates else False, - } - ) + notice_link = ApiStatic.blacklist_login(record_id=record_id) found_user.remember_me = bool(data.remember_me) found_user.save() - return { - "access_token": access_token, - "refresher_token": refresher_token, - "user": found_user, - "access_object": access_object_to_redis, - } + return access_object_to_redis raise HTTPException( status_code=401, detail="Login is not successful. Please check your credentials.", ) - - -# UserLogger.log_error( -# dict( -# user_id=found_user.id, -# domain=data.domain, -# access_key=data.access_key, -# agent=found_user.last_agent, -# ip=getattr(request, "remote_addr", None) -# or request.headers.get("X-Forwarded-For", None), -# platform=found_user.last_platform, -# login_date=str(DateTimeLocal.now()), -# is_login=True, -# ) -# ) - -# if ( -# not str(found_people.country_code).lower() -# == str(address_package.get("countryCode")).lower() -# ): -# send_email_completed = send_email( -# subject=f"Dear {found_user.nick_name}, your password has been changed.", -# receivers=[str(found_user.email)], -# html=invalid_ip_or_address_found( -# user_name=found_user.nick_name, -# address=address_package, -# notice_link=notice_link, -# ), -# ) -# if not send_email_completed: -# raise HTTPException( -# status_code=400, -# detail="An error occured at sending email. Please contact with support team.", -# ) diff --git a/databases/no_sql_models/identity.py b/databases/no_sql_models/identity.py index 6eeecff..76c0ba1 100644 --- a/databases/no_sql_models/identity.py +++ b/databases/no_sql_models/identity.py @@ -56,14 +56,41 @@ class MongoQueryIdentity: return [self.validate_timestamp(doc) for doc in result.data] if result else None def refresh_password_history_via_user(self, payload: PasswordHistoryViaUser): + self.use_collection("PasswordHistory") + password_history_item = self.mongo_engine.get_one( + match=payload.user_uu_id, field="user_uu_id" + ) + if not password_history_item: + self.mongo_engine.insert( + payload={ + "user_uu_id": str(payload.user_uu_id), + "password_history": [], + } + ) + password_history_item = self.mongo_engine.get_one( + match=payload.user_uu_id, field="user_uu_id" + ).data + password_history_list = password_history_item.get("password_history", []) + hashed_password = payload.password_add.get("password") + for password_in_history in password_history_list: + if str(password_in_history.get("password")) == str(hashed_password): + raise HTTPException( + status_code=400, + detail="Password already used. Please enter a new password that you have not used last 3 times.", + ) + + if len(password_history_list) > 3: + password_history_list.pop(0) + + password_history_list.append(payload.password_add) return self.mongo_engine.update( - field="user_uu_id", match=payload.user_uu_id, payload={ - "password_history": payload.password_history, + "password_history": password_history_list, "access_history_detail": payload.access_history_detail, - "modified_at": system_arrow.to_timestamp(system_arrow.now()), + "modified_at": system_arrow.now().timestamp(), }, + field="user_uu_id", ) def get_password_history_via_user(self, user_uu_id): @@ -99,6 +126,7 @@ class MongoQueryIdentity: result = self.mongo_engine.filter_by(payload={"user_uu_id": user_uu_id}) return [self.validate_timestamp(doc) for doc in result.data] if result else None + @staticmethod def validate_timestamp(doc): """Validate and fix timestamp fields in MongoDB documents""" if not doc: diff --git a/databases/sql_models/identity/identity.py b/databases/sql_models/identity/identity.py index 2bf8d92..3dedbe1 100644 --- a/databases/sql_models/identity/identity.py +++ b/databases/sql_models/identity/identity.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import Optional, List from fastapi import HTTPException from sqlalchemy import ( @@ -106,13 +105,11 @@ class Users(CrudCollection, UserLoginModule, SelectAction): ) password_expires_day: Mapped[int] = mapped_column( - "expires_day", Integer, server_default=str(Auth.PASSWORD_EXPIRE_DAY.days), comment="Password expires in days", ) password_expiry_begins: Mapped[TIMESTAMP] = mapped_column( - "expiry_begins", TIMESTAMP(timezone=True), server_default=func.now(), comment="Timestamp when password expiry begins", @@ -243,9 +240,15 @@ class Users(CrudCollection, UserLoginModule, SelectAction): query_engine = MongoQueryIdentity(company_uuid=self.related_company) domain_via_user = query_engine.get_domain_via_user(user_uu_id=str(self.uu_id)) + if not domain_via_user: + raise HTTPException( + status_code=401, + detail="Domain not found. Please contact the admin.", + ) + domain_via_user = domain_via_user[0] if get_main_domain: - return domain_via_user.get("main_domain") - return domain_via_user.get("other_domains_list") + return domain_via_user.get("main_domain", None) + return domain_via_user.get("other_domains_list", None) class RelationshipDutyPeople(CrudCollection): diff --git a/run_auth_service.sh b/run_auth_service.sh index 9fad406..1d79b97 100755 --- a/run_auth_service.sh +++ b/run_auth_service.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "Stopping existing containers..." -docker compose -f api-docker-compose.yml down +docker compose -f api-docker-compose.yml down --remove-orphans echo "Building and starting auth service..." -docker compose -f api-docker-compose.yml up --build wag_management_auth_service +docker compose -f api-docker-compose.yml up --build diff --git a/service_app_init/Dockerfile b/service_app_init/Dockerfile index afe4c60..4c10dc7 100644 --- a/service_app_init/Dockerfile +++ b/service_app_init/Dockerfile @@ -12,15 +12,17 @@ RUN uv pip install -r requirements.txt COPY ../service_app_init ./service_app_init -COPY ../databases ./service_app_init/databases -COPY ../api_configs ./service_app_init/api_configs -COPY ../api_events ./service_app_init/api_events -COPY ../api_library ./service_app_init/api_library -COPY ../api_validations ./service_app_init/api_validations -COPY ../api_objects ./service_app_init/api_objects -COPY ../api_services ./service_app_init/api_services +COPY databases ./service_app_init/databases +COPY api_configs ./service_app_init/api_configs +COPY api_events ./service_app_init/api_events +COPY api_library ./service_app_init/api_library +COPY api_validations ./service_app_init/api_validations +COPY api_objects ./service_app_init/api_objects +COPY api_services ./service_app_init/api_services + +COPY ApiServices ./service_app_init/ApiServices +COPY ApiServices/EventService/routers ./service_app_init/routers #COPY ../service_app/application ./service_app_init/application -#COPY ../service_app/routers ./service_app_init/routers WORKDIR /service_app_init diff --git a/service_app_init/runner.py b/service_app_init/runner.py index 6b66bee..cb2fdb6 100644 --- a/service_app_init/runner.py +++ b/service_app_init/runner.py @@ -17,147 +17,146 @@ def do_alembic(): generate_alembic_with_session(text=text) -# -# def create_one_address(): -# from databases import ( -# AddressCity, -# AddressStreet, -# AddressLocality, -# AddressDistrict, -# AddressNeighborhood, -# AddressState, -# AddressCountry, -# ) -# -# address_list = [] -# country = AddressCountry.find_or_create(country_name="TÜRKİYE", country_code="TR") -# address_list.append(country) -# state = AddressState.find_or_create( -# state_name="TÜRKİYE", -# state_code="TR", -# phone_code="90", -# country_id=country.id, -# country_uu_id=str(country.uu_id), -# ) -# address_list.append(state) -# city = AddressCity.find_or_create( -# city_name="ANKARA", -# city_code="6", -# licence_plate="06", -# state_id=state.id, -# state_uu_id=str(state.uu_id), -# ) -# address_list.append(city) -# district = AddressDistrict.find_or_create( -# district_name="ÇANKAYA", -# district_code="1231", -# city_id=city.id, -# city_uu_id=str(city.uu_id), -# ) -# address_list.append(district) -# locality = AddressLocality.find_or_create( -# locality_name="MERKEZ", -# locality_code="2431", -# type_code="3", -# type_description=None, -# district_id=district.id, -# district_uu_id=str(district.uu_id), -# ) -# address_list.append(locality) -# neighborhood = AddressNeighborhood.find_or_create( -# neighborhood_name="AYRANCI MAHALLESİ", -# neighborhood_code="1522", -# type_code="1", -# type_description="MAHALLESİ", -# locality_id=locality.id, -# locality_uu_id=str(locality.uu_id), -# ) -# address_list.append(neighborhood) -# street = AddressStreet.find_or_create( -# street_name="REŞAT NURİ CADDESİ", -# type_description="CADDESİ", -# type_code="3", -# street_code="52270", -# neighborhood_id=neighborhood.id, -# neighborhood_uu_id=str(neighborhood.uu_id), -# ) -# address_list.append(street) -# for address_single in address_list: -# address_single.save_and_confirm() -# return -# -# -# def create_application_defaults_func(create_address=False): -# from initialize_app import ( -# create_modules_and_services_and_actions, -# create_endpoints_from_api_functions, -# create_all_events_from_actions, -# create_application_defaults, -# init_api_enums_build_types, -# add_events_all_services_and_occupant_types, -# add_events_to_system_super_user, -# create_occupant_types_defaults, -# ) -# import routers -# -# try: -# create_endpoints_from_api_functions(routers=routers) -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} create_endpoints_from_api_functions Defaults Error", -# e, -# ) -# try: -# create_application_defaults() -# except Exception as e: -# print(f"{TerminalColors.WARNING} create_application_defaults Defaults Error", e) -# try: -# create_occupant_types_defaults() -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} create_occupant_types_defaults Defaults Error", e -# ) -# try: -# create_modules_and_services_and_actions() -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} create_modules_and_services_and_actions Defaults Error", -# e, -# ) -# try: -# init_api_enums_build_types() -# except Exception as e: -# print(f"{TerminalColors.WARNING} init_api_enums_build_types Defaults Error", e) -# try: -# create_all_events_from_actions() -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} create_all_events_from_actions Defaults Error", e -# ) -# try: -# add_events_all_services_and_occupant_types() -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} add_events_all_services_and_occupant_types Defaults Error", -# e, -# ) -# try: -# add_events_to_system_super_user() -# except Exception as e: -# print( -# f"{TerminalColors.WARNING} add_events_to_system_super_user Defaults Error", -# e, -# ) -# try: -# if not create_address: -# return -# create_one_address() -# except Exception as e: -# print(f"{TerminalColors.WARNING} create_one_address Defaults Error", e) -# +def create_one_address(): + from databases import ( + AddressCity, + AddressStreet, + AddressLocality, + AddressDistrict, + AddressNeighborhood, + AddressState, + AddressCountry, + ) + + address_list = [] + country = AddressCountry.find_or_create(country_name="TÜRKİYE", country_code="TR") + address_list.append(country) + state = AddressState.find_or_create( + state_name="TÜRKİYE", + state_code="TR", + phone_code="90", + country_id=country.id, + country_uu_id=str(country.uu_id), + ) + address_list.append(state) + city = AddressCity.find_or_create( + city_name="ANKARA", + city_code="6", + licence_plate="06", + state_id=state.id, + state_uu_id=str(state.uu_id), + ) + address_list.append(city) + district = AddressDistrict.find_or_create( + district_name="ÇANKAYA", + district_code="1231", + city_id=city.id, + city_uu_id=str(city.uu_id), + ) + address_list.append(district) + locality = AddressLocality.find_or_create( + locality_name="MERKEZ", + locality_code="2431", + type_code="3", + type_description=None, + district_id=district.id, + district_uu_id=str(district.uu_id), + ) + address_list.append(locality) + neighborhood = AddressNeighborhood.find_or_create( + neighborhood_name="AYRANCI MAHALLESİ", + neighborhood_code="1522", + type_code="1", + type_description="MAHALLESİ", + locality_id=locality.id, + locality_uu_id=str(locality.uu_id), + ) + address_list.append(neighborhood) + street = AddressStreet.find_or_create( + street_name="REŞAT NURİ CADDESİ", + type_description="CADDESİ", + type_code="3", + street_code="52270", + neighborhood_id=neighborhood.id, + neighborhood_uu_id=str(neighborhood.uu_id), + ) + address_list.append(street) + for address_single in address_list: + address_single.save_and_confirm() + return + + +def create_application_defaults_func(create_address=False): + from initialize_app import ( + create_modules_and_services_and_actions, + create_endpoints_from_api_functions, + create_all_events_from_actions, + create_application_defaults, + init_api_enums_build_types, + add_events_all_services_and_occupant_types, + add_events_to_system_super_user, + create_occupant_types_defaults, + ) + import routers + + try: + create_endpoints_from_api_functions(routers=routers) + except Exception as e: + print( + f"{TerminalColors.WARNING} create_endpoints_from_api_functions Defaults Error", + e, + ) + try: + create_application_defaults() + except Exception as e: + print(f"{TerminalColors.WARNING} create_application_defaults Defaults Error", e) + try: + create_occupant_types_defaults() + except Exception as e: + print( + f"{TerminalColors.WARNING} create_occupant_types_defaults Defaults Error", e + ) + try: + create_modules_and_services_and_actions() + except Exception as e: + print( + f"{TerminalColors.WARNING} create_modules_and_services_and_actions Defaults Error", + e, + ) + try: + init_api_enums_build_types() + except Exception as e: + print(f"{TerminalColors.WARNING} init_api_enums_build_types Defaults Error", e) + try: + create_all_events_from_actions() + except Exception as e: + print( + f"{TerminalColors.WARNING} create_all_events_from_actions Defaults Error", e + ) + try: + add_events_all_services_and_occupant_types() + except Exception as e: + print( + f"{TerminalColors.WARNING} add_events_all_services_and_occupant_types Defaults Error", + e, + ) + try: + add_events_to_system_super_user() + except Exception as e: + print( + f"{TerminalColors.WARNING} add_events_to_system_super_user Defaults Error", + e, + ) + try: + if not create_address: + return + create_one_address() + except Exception as e: + print(f"{TerminalColors.WARNING} create_one_address Defaults Error", e) + if __name__ == "__main__": print("Service App Initial Default Runner is running") - do_alembic() - # create_application_defaults_func(create_address=True) + # do_alembic() + create_application_defaults_func(create_address=True) print("Service App Initial Default Runner is completed") diff --git a/testers/authentication/test_login.py b/testers/authentication/test_login.py index 0989380..bc97b3c 100644 --- a/testers/authentication/test_login.py +++ b/testers/authentication/test_login.py @@ -8,28 +8,32 @@ LOGIN_ENDPOINT = f"{BASE_URL}/authentication/login" # Load test data current_dir = os.path.dirname(os.path.abspath(__file__)) -with open(os.path.join(current_dir, 'test_data.json'), 'r') as f: +with open(os.path.join(current_dir, "test_data.json"), "r") as f: TEST_DATA = json.load(f) + @pytest.fixture def test_credentials(): - return TEST_DATA['test_credentials'] + return TEST_DATA["test_credentials"] + @pytest.fixture def headers(): - return { - "Content-Type": "application/json", - "Accept": "application/json" - } + return {"Content-Type": "application/json", "Accept": "application/json"} + class TestLogin: def test_successful_login(self, test_credentials, headers): """Test successful login with provided credentials""" response = requests.post(LOGIN_ENDPOINT, json=test_credentials, headers=headers) - assert response.status_code == 200, f"Login failed with status {response.status_code}. Response: {response.text}" + assert ( + response.status_code == 200 + ), f"Login failed with status {response.status_code}. Response: {response.text}" data = response.json() assert "token" in data, f"Token not found in response. Response: {data}" - assert data.get("status") == "success", f"Status is not success. Response: {data}" + assert ( + data.get("status") == "success" + ), f"Status is not success. Response: {data}" def test_invalid_credentials(self, headers): """Test login with invalid credentials""" @@ -37,20 +41,29 @@ class TestLogin: "domain": "evyos.com.tr", "access_key": "invalid@evyos.com.tr", "password": "wrongpassword", - "remember_me": False + "remember_me": False, } - response = requests.post(LOGIN_ENDPOINT, json=invalid_credentials, headers=headers) - assert response.status_code in [401, 403], f"Expected 401 or 403, got {response.status_code}. Response: {response.text}" + response = requests.post( + LOGIN_ENDPOINT, json=invalid_credentials, headers=headers + ) + assert response.status_code in [ + 401, + 403, + ], f"Expected 401 or 403, got {response.status_code}. Response: {response.text}" def test_missing_fields(self, headers): """Test login with missing required fields""" incomplete_credentials = { "domain": "evyos.com.tr", - "access_key": "test@evyos.com.tr" + "access_key": "test@evyos.com.tr", # missing password } - response = requests.post(LOGIN_ENDPOINT, json=incomplete_credentials, headers=headers) - assert response.status_code == 422, f"Expected 422, got {response.status_code}. Response: {response.text}" + response = requests.post( + LOGIN_ENDPOINT, json=incomplete_credentials, headers=headers + ) + assert ( + response.status_code == 422 + ), f"Expected 422, got {response.status_code}. Response: {response.text}" def test_invalid_domain(self, headers): """Test login with invalid domain""" @@ -58,17 +71,26 @@ class TestLogin: "domain": "invalid-domain.com", "access_key": "test@evyos.com.tr", "password": "string", - "remember_me": False + "remember_me": False, } - response = requests.post(LOGIN_ENDPOINT, json=invalid_domain_credentials, headers=headers) - assert response.status_code in [400, 401], f"Expected 400 or 401, got {response.status_code}. Response: {response.text}" + response = requests.post( + LOGIN_ENDPOINT, json=invalid_domain_credentials, headers=headers + ) + assert response.status_code in [ + 400, + 401, + ], f"Expected 400 or 401, got {response.status_code}. Response: {response.text}" def test_malformed_json(self, headers): """Test login with malformed JSON""" response = requests.post(LOGIN_ENDPOINT, data="invalid json", headers=headers) - assert response.status_code == 422, f"Expected 422, got {response.status_code}. Response: {response.text}" - + assert ( + response.status_code == 422 + ), f"Expected 422, got {response.status_code}. Response: {response.text}" + def test_empty_request(self, headers): """Test login with empty request body""" response = requests.post(LOGIN_ENDPOINT, json={}, headers=headers) - assert response.status_code == 422, f"Expected 422, got {response.status_code}. Response: {response.text}" + assert ( + response.status_code == 422 + ), f"Expected 422, got {response.status_code}. Response: {response.text}" diff --git a/testers/mongodbTest/mongoLoginTesters/mongoLoginTest.py b/testers/mongodbTest/mongoLoginTesters/mongoLoginTest.py index 6d82d2b..299b74c 100644 --- a/testers/mongodbTest/mongoLoginTesters/mongoLoginTest.py +++ b/testers/mongodbTest/mongoLoginTesters/mongoLoginTest.py @@ -3,113 +3,6 @@ from databases.no_sql_models.mongo_database import MongoQuery from databases.no_sql_models.identity import MongoQueryIdentity -""" - - found_user: Users = Users.check_user_exits( - access_key=data.access_key, domain=data.domain - ) - access_token = found_user.generate_access_token() - query_engine = MongoQueryIdentity(company_uuid=found_user.related_company) - if found_user.check_password(password=data.password): - access_object_to_redis = RedisActions.save_access_token_to_redis( - request=request, - found_user=found_user, - domain=data.domain, - access_token=access_token, - ) - refresher_token = found_user.generate_refresher_token( - domain=data.domain, remember_me=data.remember_me - ) - headers_request = request.headers - headers_request = dict(headers_request) - headers_request["evyos-user-agent"] = headers_request.get("user-agent") - headers_request["evyos-platform"] = headers_request.get("user-agent") - headers_request["evyos-ip-ext"] = "94.54.68.158" - # found_user.last_agent = headers_request.get("evyos-user-agent", None) - # found_user.last_platform = headers_request.get("evyos-platform", None) - # found_user.last_remote_addr = headers_request.get("evyos-ip-ext", None) - # found_user.last_seen = str(system_arrow.now()) - if ext_ip := headers_request.get("evyos-ip-ext"): - agent = headers_request.get("evyos-user-agent", "") - platform = headers_request.get("evyos-platform", "") - address = requests.get(f"http://ip-api.com/json/{ext_ip}").json() - address_package = { - "city": address["city"], - "zip": address["zip"], - "country": address["country"], - "countryCode": address["countryCode"], - "region": address["region"], - "regionName": address["regionName"], - } - mongo_db = MongoQueryIdentity( - company_uuid=str(found_user.related_company).replace(" ", ""), - storage_reasoning="AccessHistory", - ) - filter_query = { - "agent": agent, - "platform": platform, - "address": address_package, - "user_id": found_user.id, - } - already_exits = mongo_db.mongo_engine.filter_by(filter_query) or None - no_address_validates = mongo_db.mongo_engine.get_all()[0] == 0 - record_id = uuid.uuid4().__str__() - notice_link = ApiStatic.blacklist_login(record_id=record_id) - found_people = People.filter_one(People.id == found_user.person_id).data - access_via_user = query_engine.update_access_history_via_user( - AccessHistoryViaUser( - **{ - "user_uu_id": found_user.uu_id.__str__(), - "access_history": { - "record_id": record_id, - "agent": agent, - "platform": platform, - "address": address_package, - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - # "is_confirmed": True if no_address_validates else False, - # "is_first": True if no_address_validates else False, - }, - } - ) - ) - if already_exits: - update_mongo = mongo_db.mongo_engine.table.update_one( - filter=filter_query, - update={ - "$set": { - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - } - }, - ) - else: - mongo_db.mongo_engine.insert( - payload={ - "user_id": found_user.id, - "record_id": record_id, - "agent": agent, - "platform": platform, - "address": address_package, - "ip": ext_ip, - "access_token": access_token, - "created_at": system_arrow.now().timestamp(), - "is_confirmed": True if no_address_validates else False, - "is_first": True if no_address_validates else False, - } - ) - found_user.remember_me = bool(data.remember_me) - found_user.save() - return { - "access_token": access_token, - "refresher_token": refresher_token, - "user": found_user, - "access_object": access_object_to_redis, - } -""" - query_engine = MongoQueryIdentity( company_uuid="5f5f4d2b-0b3f-4b1f-8f8a-3b3b7f3b7f3b", ) @@ -118,21 +11,21 @@ query_engine_mongo = MongoQuery( database_name="mongo_database", ) -payload={ +payload = { "user_uu_id": "5f5f4d2b-0b3f-4b1f-8f8a-3b3b7f3b7f3b", "other_domains_list": ["www.gluglu.com"], "main_domain": "www.gluglu.com", "modified_at": system_arrow.to_timestamp(system_arrow.now()), } query_engine.use_collection("PasswordHistory") -or_insert =query_engine.mongo_engine.find_or_insert( +or_insert = query_engine.mongo_engine.find_or_insert( payload={ "user_uu_id": str(payload.get("user_uu_id")), "password_history": [], }, field="user_uu_id", ) -print('or_insert', or_insert.data) +print("or_insert", or_insert.data) quit() get_all = query_engine_mongo.get_all() -print('get_all', get_all.data) +print("get_all", get_all.data)