join tested auth service login/select completed
This commit is contained in:
@@ -14,12 +14,14 @@ RUN poetry config virtualenvs.create false && poetry install --no-interaction --
|
||||
# Copy application code
|
||||
COPY /api_services/api_initializer /api_initializer
|
||||
COPY /api_services/api_controllers /api_controllers
|
||||
COPY /api_services/api_validations /api_validations
|
||||
COPY /api_services/schemas /schemas
|
||||
|
||||
COPY /api_services/api_middlewares /middlewares
|
||||
COPY /api_services/api_middlewares /api_middlewares
|
||||
COPY /api_services/api_builds/auth-service/endpoints /api_initializer/endpoints
|
||||
COPY /api_services/api_builds/auth-service/events /api_initializer/events
|
||||
COPY /api_services/api_builds/auth-service/validations /api_initializer/validations
|
||||
COPY /api_services/api_modules /api_modules
|
||||
|
||||
# Set Python path to include app directory
|
||||
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
155
api_services/api_builds/auth-service/endpoints/auth/router.py
Normal file
155
api_services/api_builds/auth-service/endpoints/auth/router.py
Normal file
@@ -0,0 +1,155 @@
|
||||
from typing import Union
|
||||
|
||||
from fastapi import APIRouter, Request, status, Header, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from config import api_config
|
||||
from validations.request.auth.validations import (
|
||||
RequestLogin,
|
||||
RequestResetPassword,
|
||||
RequestSelectLiving,
|
||||
RequestSelectEmployee,
|
||||
RequestCreatePassword,
|
||||
RequestChangePassword,
|
||||
RequestForgotPasswordPhone,
|
||||
RequestForgotPasswordEmail,
|
||||
RequestVerifyOTP,
|
||||
RequestApplication,
|
||||
)
|
||||
from events.auth.events import AuthHandlers
|
||||
from endpoints.index import endpoints_index
|
||||
|
||||
from api_validations.defaults.validations import CommonHeaders
|
||||
from api_middlewares.token_provider import TokenProvider
|
||||
|
||||
|
||||
auth_route = APIRouter(prefix="/authentication", tags=["Authentication Cluster"])
|
||||
|
||||
|
||||
auth_route_login = "AuthLoginViaDomainAndCreds"
|
||||
@auth_route.post(
|
||||
path="/login",
|
||||
summary="Login via domain and access key : [email] | [phone]",
|
||||
description="Login Route",
|
||||
operation_id=endpoints_index[auth_route_login]
|
||||
)
|
||||
def login(data: RequestLogin, headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Login via domain and access key : [email] | [phone]"""
|
||||
return AuthHandlers.LoginHandler.authentication_login_with_domain_and_creds(headers=headers, data=data)
|
||||
|
||||
|
||||
auth_route_select_living = "AuthSelectLiving"
|
||||
@auth_route.post(
|
||||
path="/select",
|
||||
summary="Select token object company or occupant type",
|
||||
description="Selection of users company or occupant type",
|
||||
operation_id=endpoints_index[auth_route_select_living]
|
||||
)
|
||||
def select_living(data: Union[RequestSelectLiving, RequestSelectEmployee], headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Select token object company or occupant type"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return AuthHandlers.LoginHandler.authentication_select_company_or_occupant_type(request=headers.request, data=data)
|
||||
|
||||
|
||||
auth_route_create_password = "AuthCreatePassword"
|
||||
@auth_route.post(
|
||||
path="/password/create",
|
||||
summary="Create password with access token",
|
||||
description="Create password",
|
||||
operation_id=endpoints_index[auth_route_create_password]
|
||||
)
|
||||
def create_password(data: RequestCreatePassword, headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Create password with access token"""
|
||||
# token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return AuthHandlers.PasswordHandler.create_password(password=data.password, password_token=data.password_token)
|
||||
|
||||
|
||||
auth_route_change_password = "AuthChangePassword"
|
||||
@auth_route.post(
|
||||
path="/password/change",
|
||||
summary="Change password with access token",
|
||||
description="Change password",
|
||||
operation_id=endpoints_index[auth_route_change_password]
|
||||
)
|
||||
def change_password(data: RequestChangePassword, headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Change password with access token"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_reset_password = "AuthResetPassword"
|
||||
@auth_route.post(
|
||||
path="/password/reset",
|
||||
summary="Reset password with access token",
|
||||
description="Reset password",
|
||||
operation_id=endpoints_index[auth_route_reset_password]
|
||||
)
|
||||
def reset_password(data: RequestResetPassword, headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Reset password with access token"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_logout = "AuthLogout"
|
||||
@auth_route.get(
|
||||
path="/logout",
|
||||
summary="Logout user",
|
||||
description="Logout only single session of user which domain is provided",
|
||||
operation_id=endpoints_index[auth_route_logout]
|
||||
)
|
||||
def logout(headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Logout user"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_disconnect = "AuthDisconnect"
|
||||
@auth_route.get(
|
||||
path="/disconnect",
|
||||
summary="Disconnect all sessions",
|
||||
description="Disconnect all sessions of user in access token",
|
||||
operation_id=endpoints_index[auth_route_disconnect]
|
||||
)
|
||||
def disconnect(headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Disconnect all sessions"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_check_token = "AuthCheckToken"
|
||||
@auth_route.get(
|
||||
path="/token/check",
|
||||
summary="Check if token is valid",
|
||||
description="Check if access token is valid for user",
|
||||
operation_id=endpoints_index[auth_route_check_token]
|
||||
)
|
||||
def check_token(headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Check if token is valid"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_refresh_token = "AuthRefreshToken"
|
||||
@auth_route.get(
|
||||
path="/token/refresh",
|
||||
summary="Refresh if token is valid",
|
||||
description="Refresh if access token is valid for user",
|
||||
operation_id=endpoints_index[auth_route_refresh_token]
|
||||
)
|
||||
def refresh_token(headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Refresh if token is valid"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
|
||||
|
||||
auth_route_verify_otp = "AuthVerifyOTP"
|
||||
@auth_route.get(
|
||||
path="/password/verify-otp",
|
||||
summary="Verify OTP for password reset",
|
||||
description="Verify OTP for password reset",
|
||||
operation_id=endpoints_index[auth_route_verify_otp]
|
||||
)
|
||||
def verify_otp(headers: CommonHeaders = Depends(CommonHeaders.as_dependency)):
|
||||
"""Verify OTP for password reset"""
|
||||
token_object = TokenProvider.get_dict_from_redis(token=headers.token)
|
||||
return None
|
||||
@@ -1,9 +1,13 @@
|
||||
|
||||
endpoints_index: dict = {
|
||||
"Name": "d538deb4-38f4-4913-a1af-bbef14cf6873",
|
||||
"Slot1": "c0f5ccb1-1e56-4653-af13-ec0bf5e6aa51",
|
||||
"Slot2": "034a7eb7-0186-4f48-bb8c-165c429ad5c1",
|
||||
"Slot3": "ec1f3ec3-3f28-4eaf-b89a-c463632c0b90",
|
||||
"Slot4": "2cf99f10-72f0-4c2b-98be-3082d67b950d",
|
||||
"Slot5": "15c24c6c-651b-4c5d-9c2b-5c6c6c6c6c6c",
|
||||
"AuthLoginViaDomainAndCreds": "1b94a704-7768-436d-bc20-655d92b34d83",
|
||||
"AuthSelectLiving": "585d578e-2b72-4f71-b996-530fc0613568",
|
||||
"AuthCreatePassword": "a4252148-2bac-42df-aa3a-1784f4cbd599",
|
||||
"AuthChangePassword": "d55834fa-6d7f-4007-9591-a50d3266b3aa",
|
||||
"AuthResetPassword": "29f14043-2a79-4230-bf66-a709ae954dc5",
|
||||
"AuthLogout": "616a992a-2a73-4709-a394-f043caa75937",
|
||||
"AuthDisconnect": "55dd1df1-4a00-41f9-92a9-fb776aee1cd3",
|
||||
"AuthCheckToken": "040e7a48-1ce0-432c-9bd9-5b05c2c7aef3",
|
||||
"AuthRefreshToken": "0ca54d41-d9ca-4143-b974-1050d65769b7",
|
||||
"AuthVerifyOTP": "4192e7a5-cf52-4d09-8b51-2088d77271d0",
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
from fastapi import APIRouter
|
||||
from .auth.router import auth_route
|
||||
|
||||
|
||||
def get_routes() -> list[APIRouter]:
|
||||
return []
|
||||
"""Get all routes"""
|
||||
return [auth_route]
|
||||
|
||||
|
||||
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
|
||||
"""Get all safe endpoint urls"""
|
||||
return [
|
||||
("/", "GET"),
|
||||
("/docs", "GET"),
|
||||
("/redoc", "GET"),
|
||||
("/openapi.json", "GET"),
|
||||
("/metrics", "GET"),
|
||||
("/authentication/login", "POST"),
|
||||
("/authentication/password/reset", "POST"),
|
||||
("/authentication/password/create", "POST"),
|
||||
("/authentication/password/verify-otp", "POST"),
|
||||
]
|
||||
|
||||
599
api_services/api_builds/auth-service/events/auth/events.py
Normal file
599
api_services/api_builds/auth-service/events/auth/events.py
Normal file
@@ -0,0 +1,599 @@
|
||||
import arrow
|
||||
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from config import api_config
|
||||
from schemas import (
|
||||
Users,
|
||||
People,
|
||||
BuildLivingSpace,
|
||||
BuildParts,
|
||||
OccupantTypes,
|
||||
Employees,
|
||||
Addresses,
|
||||
Companies,
|
||||
Staff,
|
||||
Duty,
|
||||
Duties,
|
||||
Departments,
|
||||
Event2Employee,
|
||||
Application2Occupant,
|
||||
Event2Occupant,
|
||||
Application2Employee,
|
||||
RelationshipEmployee2Build,
|
||||
)
|
||||
from api_modules.token.password_module import PasswordModule
|
||||
from api_controllers.redis.database import RedisActions
|
||||
from api_controllers.mongo.database import mongo_handler
|
||||
from api_validations.token.validations import EmployeeTokenObject, OccupantTokenObject, CompanyToken, OccupantToken, UserType
|
||||
from api_validations.defaults.validations import CommonHeaders
|
||||
from validations.password.validations import PasswordHistoryViaUser
|
||||
|
||||
|
||||
TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
|
||||
|
||||
|
||||
class RedisHandlers:
|
||||
|
||||
AUTH_TOKEN: str = "AUTH_TOKEN"
|
||||
|
||||
@classmethod
|
||||
def process_redis_object(cls, redis_object: Dict[str, Any]) -> TokenDictType:
|
||||
"""Process Redis object and return appropriate token object."""
|
||||
if not redis_object.get("selected_company"):
|
||||
redis_object["selected_company"] = None
|
||||
if not redis_object.get("selected_occupant"):
|
||||
redis_object["selected_occupant"] = None
|
||||
if redis_object.get("user_type") == UserType.employee.value:
|
||||
return EmployeeTokenObject(**redis_object)
|
||||
elif redis_object.get("user_type") == UserType.occupant.value:
|
||||
return OccupantTokenObject(**redis_object)
|
||||
raise ValueError("Invalid user type")
|
||||
|
||||
@classmethod
|
||||
def get_object_from_redis(cls, access_token: str) -> TokenDictType:
|
||||
redis_response = RedisActions.get_json(list_keys=[RedisHandlers.AUTH_TOKEN, access_token, "*"])
|
||||
if not redis_response.status:
|
||||
raise ValueError("EYS_0001")
|
||||
if redis_object := redis_response.first:
|
||||
return cls.process_redis_object(redis_object)
|
||||
raise ValueError("EYS_0002")
|
||||
|
||||
@classmethod
|
||||
def set_object_to_redis(cls, user: Users, token, header_info):
|
||||
result_delete = RedisActions.delete(list_keys=[RedisHandlers.AUTH_TOKEN, "*", str(user.uu_id)])
|
||||
generated_access_token = PasswordModule.generate_access_token()
|
||||
keys = [RedisHandlers.AUTH_TOKEN, generated_access_token, str(user.uu_id)]
|
||||
RedisActions.set_json(list_keys=keys, value={**token, **header_info}, expires={"hours": 1, "minutes": 30})
|
||||
return generated_access_token
|
||||
|
||||
@classmethod
|
||||
def update_token_at_redis(cls, token: str, add_payload: Union[CompanyToken, OccupantToken]):
|
||||
if already_token_data := RedisActions.get_json(list_keys=[RedisHandlers.AUTH_TOKEN, token, "*"]).first:
|
||||
already_token = cls.process_redis_object(already_token_data)
|
||||
if already_token.is_employee and isinstance(add_payload, CompanyToken):
|
||||
already_token.selected_company = add_payload
|
||||
elif already_token.is_occupant and isinstance(add_payload, OccupantToken):
|
||||
already_token.selected_occupant = add_payload
|
||||
result = RedisActions.set_json(
|
||||
list_keys=[RedisHandlers.AUTH_TOKEN, token, str(already_token.user_uu_id)], value=already_token.model_dump(), expires={"hours": 1, "minutes": 30}
|
||||
)
|
||||
return result.first
|
||||
raise ValueError("Something went wrong")
|
||||
|
||||
|
||||
class UserHandlers:
|
||||
|
||||
@staticmethod
|
||||
def check_user_exists(access_key: str, db_session) -> Users:
|
||||
"""Check if the user exists in the database."""
|
||||
Users.set_session(db_session)
|
||||
if "@" in access_key:
|
||||
found_user: Users = Users.query.filter(Users.email == access_key.lower()).first()
|
||||
else:
|
||||
found_user: Users = Users.query.filter(Users.phone_number == access_key.replace(" ", "")).first()
|
||||
if not found_user:
|
||||
raise ValueError("EYS_0003")
|
||||
return found_user
|
||||
|
||||
@staticmethod
|
||||
def check_password_valid(domain: str, id_: str, password: str, password_hashed: str) -> bool:
|
||||
"""Check if the password is valid."""
|
||||
if PasswordModule.check_password(domain=domain, id_=id_, password=password, password_hashed=password_hashed):
|
||||
return True
|
||||
raise ValueError("EYS_0004")
|
||||
|
||||
@staticmethod
|
||||
def update_password():
|
||||
return
|
||||
|
||||
|
||||
class LoginHandler:
|
||||
|
||||
@staticmethod
|
||||
def is_occupant(email: str):
|
||||
return not str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||
|
||||
@staticmethod
|
||||
def is_employee(email: str):
|
||||
return str(email).split("@")[1] == api_config.ACCESS_EMAIL_EXT
|
||||
|
||||
@classmethod
|
||||
def do_employee_login(cls, headers: CommonHeaders, data: Any, db_session):
|
||||
"""Handle employee login."""
|
||||
user_handler, other_domains_list, main_domain = UserHandlers(), [], ""
|
||||
found_user = user_handler.check_user_exists(access_key=data.access_key, db_session=db_session)
|
||||
with mongo_handler.collection(f"{str(found_user.related_company)}*Domain") as collection:
|
||||
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
if not result:
|
||||
raise ValueError("EYS_00087")
|
||||
other_domains_list = result.get("other_domains_list", [])
|
||||
main_domain = result.get("main_domain", None)
|
||||
if headers.domain not in other_domains_list or not main_domain:
|
||||
raise ValueError("EYS_00088")
|
||||
|
||||
if not user_handler.check_password_valid(domain=main_domain, id_=str(found_user.uu_id), password=data.password, password_hashed=found_user.hash_password):
|
||||
raise ValueError("EYS_0005")
|
||||
list_of_returns = (
|
||||
Employees.id, Employees.uu_id, People.id, People.uu_id, Users.id, Users.uu_id, Companies.id, Companies.uu_id,
|
||||
Departments.id, Departments.uu_id, Duty.id, Duty.uu_id, Companies.public_name, Companies.company_type, Duty.duty_name,
|
||||
Addresses.letter_address
|
||||
)
|
||||
|
||||
list_employee_query = db_session.query(*list_of_returns
|
||||
).join(Staff, Staff.id == Employees.staff_id
|
||||
).join(People, People.id == Employees.people_id
|
||||
).join(Duties, Duties.id == Staff.duties_id
|
||||
).join(Duty, Duty.id == Duties.duties_id
|
||||
).join(Departments, Departments.id == Duties.department_id
|
||||
).join(Companies, Companies.id == Departments.company_id
|
||||
).join(Users, Users.person_id == People.id
|
||||
).outerjoin(Addresses, Addresses.id == Companies.official_address_id
|
||||
).filter(Employees.people_id == found_user.person_id)
|
||||
list_employees, list_employees_query_all = [], list_employee_query.all()
|
||||
if not list_employees_query_all:
|
||||
ValueError("No Employee found for this user")
|
||||
|
||||
for employee in list_employees_query_all:
|
||||
single_employee = {}
|
||||
for ix, returns in enumerate(list_of_returns):
|
||||
single_employee[str(returns)] = employee[ix]
|
||||
list_employees.append(single_employee)
|
||||
companies_uu_id_list, companies_id_list, companies_list, duty_uu_id_list, duty_id_list = [], [], [], [], []
|
||||
for list_employee in list_employees:
|
||||
companies_id_list.append(int(list_employee["Companies.id"]))
|
||||
companies_uu_id_list.append(str(list_employee["Companies.uu_id"]))
|
||||
duty_uu_id_list.append(str(list_employee["Duty.uu_id"]))
|
||||
duty_id_list.append(int(list_employee["Duty.id"]))
|
||||
companies_list.append({
|
||||
"uu_id": str(list_employee["Companies.uu_id"]), "public_name": list_employee["Companies.public_name"],
|
||||
"company_type": list_employee["Companies.company_type"], "company_address": list_employee["Addresses.letter_address"],
|
||||
"duty": list_employee["Duty.duty_name"]
|
||||
})
|
||||
model_value = EmployeeTokenObject(
|
||||
user_type=UserType.employee.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
user_id=found_user.id,
|
||||
person_id=found_user.person_id,
|
||||
person_uu_id=str(list_employees[0]["People.uu_id"]),
|
||||
request=dict(headers.request.headers),
|
||||
domain_list=other_domains_list,
|
||||
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,
|
||||
).model_dump()
|
||||
set_to_redis_dict = dict(
|
||||
user=found_user,
|
||||
token=model_value,
|
||||
header_info=dict(language=headers.language, domain=headers.domain, timezone=headers.timezone),
|
||||
)
|
||||
redis_handler = RedisHandlers()
|
||||
user_dict = found_user.get_dict()
|
||||
person_dict = found_user.person.get_dict()
|
||||
if access_token := redis_handler.set_object_to_redis(**set_to_redis_dict):
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"user_type": UserType.employee.name,
|
||||
"user": {
|
||||
"uuid": user_dict["uu_id"],
|
||||
"avatar": user_dict["avatar"],
|
||||
"email": user_dict["email"],
|
||||
"phone_number": user_dict["phone_number"],
|
||||
"user_tag": user_dict["user_tag"],
|
||||
"password_expiry_begins": str(arrow.get(user_dict["password_expiry_begins"]).shift(days=int(user_dict["password_expires_day"]))),
|
||||
"person": {
|
||||
"uuid": person_dict["uu_id"],
|
||||
"firstname": person_dict["firstname"],
|
||||
"surname": person_dict["surname"],
|
||||
"middle_name": person_dict["middle_name"],
|
||||
"sex_code": person_dict["sex_code"],
|
||||
"person_tag": person_dict["person_tag"],
|
||||
"country_code": person_dict["country_code"],
|
||||
"birth_date": person_dict["birth_date"],
|
||||
},
|
||||
},
|
||||
"selection_list": companies_list,
|
||||
}
|
||||
raise ValueError("Something went wrong")
|
||||
|
||||
@classmethod
|
||||
def do_occupant_login(cls, request: Any, data: Any, db_session, extra_dict: Optional[Dict[str, Any]] = None):
|
||||
"""
|
||||
Handle occupant login.
|
||||
"""
|
||||
language = extra_dict.get("language", "tr")
|
||||
domain = extra_dict.get("domain", None)
|
||||
timezone = extra_dict.get("tz", None) or "GMT+3"
|
||||
|
||||
user_handler = UserHandlers()
|
||||
found_user = user_handler.check_user_exists(
|
||||
access_key=data.access_key, db_session=db_session
|
||||
)
|
||||
other_domains_list, main_domain = [], ""
|
||||
with mongo_handler.collection(
|
||||
f"{str(found_user.related_company)}*Domain"
|
||||
) as collection:
|
||||
result = collection.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
if not result:
|
||||
raise ValueError("EYS_00087")
|
||||
other_domains_list = result.get("other_domains_list", [])
|
||||
main_domain = result.get("main_domain", None)
|
||||
if domain not in other_domains_list or not main_domain:
|
||||
raise ValueError("EYS_00088")
|
||||
|
||||
if not user_handler.check_password_valid(
|
||||
domain=main_domain,
|
||||
id_=str(found_user.uu_id),
|
||||
password=data.password,
|
||||
password_hashed=found_user.hash_password,
|
||||
):
|
||||
raise ValueError("EYS_0005")
|
||||
|
||||
occupants_selection_dict: Dict[str, Any] = {}
|
||||
living_spaces: list[BuildLivingSpace] = BuildLivingSpace.filter_all(
|
||||
BuildLivingSpace.person_id == found_user.person_id, db=db_session
|
||||
).data
|
||||
|
||||
if not living_spaces:
|
||||
raise ValueError("EYS_0006")
|
||||
for living_space in living_spaces:
|
||||
build_part = BuildParts.filter_one(
|
||||
BuildParts.id == living_space.build_parts_id,
|
||||
db=db_session,
|
||||
).data
|
||||
if not build_part:
|
||||
raise ValueError("EYS_0007")
|
||||
|
||||
build = build_part.buildings
|
||||
occupant_type = OccupantTypes.filter_by_one(
|
||||
id=living_space.occupant_type_id,
|
||||
db=db_session,
|
||||
system=True,
|
||||
).data
|
||||
occupant_data = {
|
||||
"build_living_space_uu_id": str(living_space.uu_id),
|
||||
"part_uu_id": str(build_part.uu_id),
|
||||
"part_name": build_part.part_name(db=db_session),
|
||||
"part_level": build_part.part_level,
|
||||
"occupant_uu_id": str(occupant_type.uu_id),
|
||||
"description": occupant_type.occupant_description,
|
||||
"code": occupant_type.occupant_code,
|
||||
}
|
||||
|
||||
build_key = str(build.uu_id)
|
||||
if build_key not in occupants_selection_dict:
|
||||
occupants_selection_dict[build_key] = {
|
||||
"build_uu_id": build_key,
|
||||
"build_name": build.build_name,
|
||||
"build_no": build.build_no,
|
||||
"occupants": [occupant_data],
|
||||
}
|
||||
else:
|
||||
occupants_selection_dict[build_key]["occupants"].append(occupant_data)
|
||||
|
||||
person = found_user.person
|
||||
model_value = OccupantTokenObject(
|
||||
user_type=UserType.occupant.value,
|
||||
user_uu_id=str(found_user.uu_id),
|
||||
user_id=found_user.id,
|
||||
person_id=person.id,
|
||||
person_uu_id=str(person.uu_id),
|
||||
domain_list=other_domains_list,
|
||||
request=dict(request.headers),
|
||||
available_occupants=occupants_selection_dict,
|
||||
).model_dump()
|
||||
redis_handler = RedisHandlers()
|
||||
if access_token := redis_handler.set_object_to_redis(
|
||||
user=found_user,
|
||||
token=model_value,
|
||||
header_info=dict(language=language, domain=domain, timezone=timezone),
|
||||
):
|
||||
return {
|
||||
"access_token": access_token,
|
||||
"user_type": UserType.occupant.name,
|
||||
"selection_list": occupants_selection_dict,
|
||||
}
|
||||
raise ValueError("Something went wrong")
|
||||
|
||||
@classmethod
|
||||
def authentication_login_with_domain_and_creds(cls, headers: CommonHeaders, data: Any):
|
||||
"""
|
||||
Authenticate user with domain and credentials.
|
||||
|
||||
Args:
|
||||
headers: CommonHeaders object
|
||||
data: Request body containing login credentials
|
||||
{
|
||||
"access_key": "karatay.berkay.sup@evyos.com.tr",
|
||||
"password": "string",
|
||||
"remember_me": false
|
||||
}
|
||||
Returns:
|
||||
SuccessResponse containing authentication token and user info
|
||||
"""
|
||||
|
||||
with Users.new_session() as db_session:
|
||||
if cls.is_employee(data.access_key):
|
||||
return cls.do_employee_login(headers=headers, data=data, db_session=db_session)
|
||||
elif cls.is_occupant(data.access_key):
|
||||
return cls.do_occupant_login(headers=headers, data=data, db_session=db_session)
|
||||
else:
|
||||
raise ValueError("Invalid email format")
|
||||
|
||||
@classmethod
|
||||
def raise_error_if_request_has_no_token(cls, request: Any) -> None:
|
||||
"""Validate request has required token headers."""
|
||||
if not hasattr(request, "headers"):
|
||||
raise ValueError("Request has no headers")
|
||||
if not request.headers.get(api_config.ACCESS_TOKEN_TAG):
|
||||
raise ValueError("Request has no access token")
|
||||
|
||||
@classmethod
|
||||
def get_access_token_from_request(cls, request: Any) -> str:
|
||||
"""Extract access token from request headers."""
|
||||
cls.raise_error_if_request_has_no_token(request=request)
|
||||
return request.headers.get(api_config.ACCESS_TOKEN_TAG)
|
||||
|
||||
@classmethod
|
||||
def handle_employee_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||
with Users.new_session() as db_session:
|
||||
if data.company_uu_id not in token_dict.companies_uu_id_list:
|
||||
ValueError("EYS_0011")
|
||||
list_of_returns = (
|
||||
Employees.id, Employees.uu_id, People.id, People.uu_id, Users.id, Users.uu_id, Companies.id, Companies.uu_id,
|
||||
Departments.id, Departments.uu_id, Duty.id, Duty.uu_id, Addresses.id, Addresses.letter_address, Staff.id, Staff.uu_id,
|
||||
Duties.id, Duties.uu_id,
|
||||
)
|
||||
|
||||
selected_company_query = db_session.query(*list_of_returns
|
||||
).join(Staff, Staff.id == Employees.staff_id
|
||||
).join(People, People.id == Employees.people_id
|
||||
).join(Duties, Duties.id == Staff.duties_id
|
||||
).join(Duty, Duty.id == Duties.duties_id
|
||||
).join(Departments, Departments.id == Duties.department_id
|
||||
).join(Companies, Companies.id == Departments.company_id
|
||||
).join(Users, Users.person_id == People.id
|
||||
).outerjoin(Addresses, Addresses.id == Companies.official_address_id
|
||||
).filter(Companies.uu_id == data.company_uu_id, Users.id == token_dict.user_id)
|
||||
|
||||
selected_company_first = selected_company_query.first()
|
||||
if not selected_company_first:
|
||||
ValueError("Selected company not found")
|
||||
|
||||
result_with_keys_dict = {}
|
||||
for ix, selected_company_item in enumerate(selected_company_first):
|
||||
result_with_keys_dict[str(list_of_returns[ix])] = selected_company_item
|
||||
|
||||
if not selected_company_first:
|
||||
ValueError("EYS_0010")
|
||||
|
||||
# Get reachable events
|
||||
reachable_event_codes = Event2Employee.get_event_codes(employee_id=int(result_with_keys_dict['Employees.id']), db=db_session)
|
||||
# Get reachable applications
|
||||
reachable_app_codes = Application2Employee.get_application_codes(employee_id=int(result_with_keys_dict['Employees.id']), db=db_session)
|
||||
|
||||
company_token = CompanyToken(
|
||||
company_uu_id=str(result_with_keys_dict['Companies.uu_id']),
|
||||
company_id=int(result_with_keys_dict['Companies.id']),
|
||||
department_id=int(result_with_keys_dict['Departments.id']),
|
||||
department_uu_id=str(result_with_keys_dict['Departments.uu_id']),
|
||||
duty_id=int(result_with_keys_dict['Duty.id']),
|
||||
duty_uu_id=str(result_with_keys_dict['Duty.uu_id']),
|
||||
bulk_duties_id=int(result_with_keys_dict['Duties.id']),
|
||||
staff_id=int(result_with_keys_dict['Staff.id']),
|
||||
staff_uu_id=str(result_with_keys_dict['Staff.uu_id']),
|
||||
employee_id=int(result_with_keys_dict['Employees.id']),
|
||||
employee_uu_id=str(result_with_keys_dict['Employees.uu_id']),
|
||||
reachable_event_codes=reachable_event_codes,
|
||||
reachable_app_codes=reachable_app_codes,
|
||||
)
|
||||
redis_handler = RedisHandlers()
|
||||
redis_result = redis_handler.update_token_at_redis(token=access_token, add_payload=company_token)
|
||||
return {"selected_uu_id": data.company_uu_id}
|
||||
|
||||
@classmethod
|
||||
def handle_occupant_selection(cls, access_token: str, data: Any, token_dict: TokenDictType):
|
||||
"""Handle occupant type selection"""
|
||||
with BuildLivingSpace.new_session() as db:
|
||||
# Get selected occupant type
|
||||
selected_build_living_space: BuildLivingSpace = BuildLivingSpace.filter_one(BuildLivingSpace.uu_id == data.build_living_space_uu_id, db=db).data
|
||||
if not selected_build_living_space:
|
||||
raise ValueError("EYS_0012")
|
||||
|
||||
# Get reachable events
|
||||
reachable_event_codes = Event2Occupant.get_event_codes(build_living_space_id=selected_build_living_space.id, db=db)
|
||||
occupant_type = OccupantTypes.filter_one_system(OccupantTypes.id == selected_build_living_space.occupant_type_id, db=db).data
|
||||
build_part = BuildParts.filter_one(BuildParts.id == selected_build_living_space.build_parts_id, db=db)
|
||||
build = build_part.buildings
|
||||
reachable_app_codes = Application2Occupant.get_application_codes(build_living_space_id=selected_build_living_space.id, db=db)
|
||||
# responsible_employee = Employees.filter_one(
|
||||
# Employees.id == build_part.responsible_employee_id,
|
||||
# db=db,
|
||||
# ).data
|
||||
# related_company = RelationshipEmployee2Build.filter_one(
|
||||
# RelationshipEmployee2Build.member_id == build.id,
|
||||
# db=db,
|
||||
# ).data
|
||||
# Get company
|
||||
# company_related = Companies.filter_one(
|
||||
# Companies.id == related_company.company_id,
|
||||
# db=db,
|
||||
# ).data
|
||||
|
||||
# Create occupant token
|
||||
occupant_token = OccupantToken(
|
||||
living_space_id=selected_build_living_space.id,
|
||||
living_space_uu_id=selected_build_living_space.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_codes=reachable_event_codes,
|
||||
reachable_app_codes=reachable_app_codes,
|
||||
)
|
||||
redis_handler = RedisHandlers()
|
||||
redis_handler.update_token_at_redis(token=access_token, add_payload=occupant_token)
|
||||
return {"selected_uu_id": occupant_token.living_space_uu_id}
|
||||
|
||||
@classmethod # Requires auth context
|
||||
def authentication_select_company_or_occupant_type(cls, request: Any, data: Any):
|
||||
"""
|
||||
Handle selection of company or occupant type
|
||||
{"data": {"build_living_space_uu_id": ""}} | {"data": {"company_uu_id": ""}}
|
||||
{
|
||||
"data": {"company_uu_id": "e9869a25-ba4d-49dc-bb0d-8286343b184b"}
|
||||
}
|
||||
{
|
||||
"data": {"build_living_space_uu_id": "e9869a25-ba4d-49dc-bb0d-8286343b184b"}
|
||||
}
|
||||
"""
|
||||
access_token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
|
||||
if not access_token:
|
||||
raise ValueError("EYS_0001")
|
||||
|
||||
token_object = RedisHandlers.get_object_from_redis(access_token=access_token)
|
||||
if token_object.is_employee:
|
||||
return cls.handle_employee_selection(access_token=access_token, data=data, token_dict=token_object)
|
||||
elif token_object.is_occupant:
|
||||
return cls.handle_occupant_selection(access_token=access_token, data=data, token_dict=token_object)
|
||||
|
||||
@classmethod
|
||||
def authentication_check_token_valid(cls, domain, access_token: str) -> bool:
|
||||
redis_handler = RedisHandlers()
|
||||
if auth_token := redis_handler.get_object_from_redis(access_token=access_token):
|
||||
if auth_token.is_employee:
|
||||
if domain not in auth_token.domain_list:
|
||||
raise ValueError("EYS_00112")
|
||||
return True
|
||||
elif auth_token.is_occupant:
|
||||
if domain not in auth_token.domain_list:
|
||||
raise ValueError("EYS_00113")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class PasswordHandler:
|
||||
|
||||
@staticmethod
|
||||
def create_password(password, password_token=None):
|
||||
with Users.new_session() as db_session:
|
||||
Users.set_session(db_session)
|
||||
found_user = Users.query.filter(Users.password_token == password_token).first()
|
||||
if not found_user:
|
||||
raise ValueError("EYS_0031")
|
||||
|
||||
if found_user.password_token:
|
||||
replace_day = 0
|
||||
try:
|
||||
replace_day = int(str(found_user.password_expires_day or 0).split(",")[0].replace(" days", ""))
|
||||
except Exception as e:
|
||||
err = e
|
||||
token_is_expired = arrow.now() >= arrow.get(str(found_user.password_expiry_begins)).shift(days=replace_day)
|
||||
if not str(password_token) == str(found_user.password_token) or token_is_expired:
|
||||
raise ValueError("EYS_0032")
|
||||
|
||||
collection_name = f"{found_user.related_company}*Domain"
|
||||
with mongo_handler.collection(collection_name) as mongo_engine:
|
||||
domain_via_user = mongo_engine.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
if not domain_via_user:
|
||||
raise ValueError("EYS_0024")
|
||||
domain_via_user = domain_via_user.get("main_domain", None)
|
||||
new_password_dict = {
|
||||
"password": PasswordModule.create_hashed_password(domain=domain_via_user, id_=str(found_user.uu_id), password=password),
|
||||
"date": str(arrow.now().date()),
|
||||
}
|
||||
history_dict = PasswordHistoryViaUser(user_uu_id=str(found_user.uu_id), password_add=new_password_dict, access_history_detail={"request": "", "ip": ""})
|
||||
found_user.password_expiry_begins = str(arrow.now())
|
||||
found_user.hash_password = new_password_dict.get("password")
|
||||
found_user.password_token = "" if found_user.password_token else ""
|
||||
|
||||
collection_name = f"{found_user.related_company}*PasswordHistory"
|
||||
with mongo_handler.collection(collection_name) as mongo_engine_sc:
|
||||
password_history_item = mongo_engine_sc.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
if not password_history_item:
|
||||
mongo_engine_sc.insert_one(document={"user_uu_id": str(found_user.uu_id), "password_history": []})
|
||||
password_history_item = mongo_engine_sc.find_one({"user_uu_id": str(found_user.uu_id)})
|
||||
password_history_list = password_history_item.get("password_history", [])
|
||||
hashed_password = history_dict.password_add.get("password")
|
||||
for password_in_history in password_history_list:
|
||||
print('password_history_list', password_history_list, password_in_history)
|
||||
if str(password_in_history.get("password")) == str(hashed_password):
|
||||
raise ValueError("EYS_0032")
|
||||
if len(password_history_list) > 3:
|
||||
password_history_list.pop(0)
|
||||
password_history_list.append(history_dict.password_add)
|
||||
return mongo_engine_sc.update_one(
|
||||
filter={"user_uu_id": str(found_user.uu_id)},
|
||||
update={"$set": {
|
||||
"password_history": password_history_list, "modified_at": arrow.now().timestamp(), "access_history_detail": history_dict.access_history_detail
|
||||
}}, upsert=True,
|
||||
)
|
||||
found_user.save(db=db_session)
|
||||
return found_user
|
||||
|
||||
|
||||
class PageHandlers:
|
||||
|
||||
@classmethod
|
||||
def retrieve_valid_page_via_token(cls, access_token: str, page_url: str) -> str:
|
||||
"""
|
||||
Retrieve valid page via token. {access_token: "string", page_url: "string"} | Results: str(application)
|
||||
"""
|
||||
if result := RedisHandlers.get_object_from_redis(access_token=access_token):
|
||||
if result.is_employee:
|
||||
if result.selected_company and result.selected_company.reachable_app_codes:
|
||||
if application := result.selected_company.reachable_app_codes.get(page_url, None):
|
||||
return application
|
||||
elif result.is_occupant:
|
||||
if result.selected_occupant and result.selected_occupant.reachable_app_codes:
|
||||
if application := result.selected_occupant.reachable_app_codes.get(page_url, None):
|
||||
return application
|
||||
raise ValueError("EYS_0013")
|
||||
|
||||
@classmethod
|
||||
def retrieve_valid_sites_via_token(cls, access_token: str) -> list:
|
||||
"""
|
||||
Retrieve valid pages via token. {"access_token": "string"} | Results: list(sites)
|
||||
"""
|
||||
if result := RedisHandlers.get_object_from_redis(access_token=access_token):
|
||||
if result.is_employee:
|
||||
if result.selected_company and result.selected_company.reachable_app_codes:
|
||||
return result.selected_company.reachable_app_codes.keys()
|
||||
elif result.is_occupant:
|
||||
if result.selected_occupant and result.selected_occupant.reachable_app_codes:
|
||||
return result.selected_occupant.reachable_app_codes.keys()
|
||||
raise ValueError("EYS_0013")
|
||||
|
||||
|
||||
class AuthHandlers:
|
||||
|
||||
LoginHandler: LoginHandler = LoginHandler()
|
||||
PasswordHandler: PasswordHandler = PasswordHandler()
|
||||
PageHandlers: PageHandlers = PageHandlers()
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class DomainViaUser(BaseModel):
|
||||
user_uu_id: str
|
||||
main_domain: str
|
||||
other_domains_list: Optional[list] = None
|
||||
|
||||
|
||||
class PasswordHistoryViaUser(BaseModel):
|
||||
user_uu_id: str
|
||||
password_add: dict
|
||||
access_history_detail: Optional[dict]
|
||||
|
||||
|
||||
class AccessHistoryViaUser(BaseModel):
|
||||
user_uu_id: str
|
||||
access_history: dict
|
||||
@@ -0,0 +1,78 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
|
||||
class RequestLogin(BaseModel):
|
||||
access_key: str
|
||||
password: str
|
||||
remember_me: Optional[bool]
|
||||
|
||||
|
||||
class RequestVerifyOTP(BaseModel):
|
||||
token: str
|
||||
otp: str
|
||||
|
||||
|
||||
class RequestApplication(BaseModel):
|
||||
page_url: str # /building/create
|
||||
|
||||
|
||||
class RequestSelectEmployee(BaseModel):
|
||||
|
||||
company_uu_id: str
|
||||
|
||||
@property
|
||||
def is_employee(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_occupant(self):
|
||||
return False
|
||||
|
||||
|
||||
class RequestResetPassword(BaseModel):
|
||||
password_token: str
|
||||
password: str
|
||||
re_password: str
|
||||
|
||||
|
||||
class RequestSelectLiving(BaseModel):
|
||||
|
||||
build_living_space_uu_id: str
|
||||
|
||||
@property
|
||||
def is_employee(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_occupant(self):
|
||||
return True
|
||||
|
||||
|
||||
class RequestCreatePassword(BaseModel):
|
||||
password_token: str
|
||||
password: str
|
||||
re_password: str
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.password == self.re_password
|
||||
|
||||
|
||||
class RequestChangePassword(BaseModel):
|
||||
old_password: str
|
||||
password: str
|
||||
re_password: str
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.password == self.re_password
|
||||
|
||||
|
||||
class RequestForgotPasswordEmail(BaseModel):
|
||||
email: str
|
||||
|
||||
|
||||
class RequestForgotPasswordPhone(BaseModel):
|
||||
phone_number: str
|
||||
@@ -14,6 +14,7 @@ RUN poetry config virtualenvs.create false && poetry install --no-interaction --
|
||||
# Copy application code
|
||||
COPY /api_services/api_controllers /api_controllers
|
||||
COPY /api_services/schemas /schemas
|
||||
COPY /api_services/api_modules /api_modules
|
||||
|
||||
COPY /api_services/api_builds/initial-service /initial-service
|
||||
COPY /api_services/api_builds/initial-service /
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import arrow
|
||||
|
||||
from modules.Token.password_module import PasswordModule
|
||||
from api_modules.token.password_module import PasswordModule
|
||||
from api_controllers.mongo.database import mongo_handler
|
||||
from schemas import (
|
||||
Companies,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import arrow
|
||||
from modules.Token.password_module import PasswordModule
|
||||
from api_modules.token.password_module import PasswordModule
|
||||
from api_controllers.mongo.database import mongo_handler
|
||||
from schemas import (
|
||||
Addresses,
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Configs(BaseSettings):
|
||||
"""
|
||||
ApiTemplate configuration settings.
|
||||
"""
|
||||
|
||||
ACCESS_TOKEN_LENGTH: int = 90
|
||||
REFRESHER_TOKEN_LENGTH: int = 144
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="API_")
|
||||
|
||||
|
||||
token_config = Configs()
|
||||
@@ -1,44 +0,0 @@
|
||||
import hashlib
|
||||
import uuid
|
||||
import secrets
|
||||
import random
|
||||
|
||||
from .config import token_config
|
||||
|
||||
|
||||
class PasswordModule:
|
||||
|
||||
@staticmethod
|
||||
def generate_random_uu_id(str_std: bool = True):
|
||||
return str(uuid.uuid4()) if str_std else uuid.uuid4()
|
||||
|
||||
@staticmethod
|
||||
def generate_token(length=32) -> str:
|
||||
letters = "abcdefghijklmnopqrstuvwxyz"
|
||||
merged_letters = [letter for letter in letters] + [
|
||||
letter.upper() for letter in letters
|
||||
]
|
||||
token_generated = secrets.token_urlsafe(length)
|
||||
for i in str(token_generated):
|
||||
if i not in merged_letters:
|
||||
token_generated = token_generated.replace(
|
||||
i, random.choice(merged_letters), 1
|
||||
)
|
||||
return token_generated
|
||||
raise ValueError("EYS_0004")
|
||||
|
||||
@classmethod
|
||||
def generate_access_token(cls) -> str:
|
||||
return cls.generate_token(int(token_config.ACCESS_TOKEN_LENGTH))
|
||||
|
||||
@classmethod
|
||||
def generate_refresher_token(cls) -> str:
|
||||
return cls.generate_token(int(token_config.REFRESHER_TOKEN_LENGTH))
|
||||
|
||||
@staticmethod
|
||||
def create_hashed_password(domain: str, id_: str, password: str) -> str:
|
||||
return hashlib.sha256(f"{domain}:{id_}:{password}".encode("utf-8")).hexdigest()
|
||||
|
||||
@classmethod
|
||||
def check_password(cls, domain, id_, password, password_hashed) -> bool:
|
||||
return cls.create_hashed_password(domain, id_, password) == password_hashed
|
||||
@@ -0,0 +1,19 @@
|
||||
endpoints_index: dict = {
|
||||
"AccountRecordsAll": "d538deb4-38f4-4913-a1af-bbef14cf6873",
|
||||
"AccountRecordsMonthly": "c0f5ccb1-1e56-4653-af13-ec0bf5e6aa51",
|
||||
"EventsListAvailable": "034a7eb7-0186-4f48-bb8c-165c429ad5c1",
|
||||
"EventsListAppended": "ec1f3ec3-3f28-4eaf-b89a-c463632c0b90",
|
||||
"EventServiceRegister": "2cf99f10-72f0-4c2b-98be-3082d67b950d",
|
||||
"EventServiceUnRegister": "15c24c6c-651b-4c5d-9c2b-5c6c6c6c6c6c",
|
||||
"EventBindExtraEmployee": "74cafa62-674e-41da-959d-1238ad4a443c",
|
||||
"EventBindExtraOccupant": "480bee12-8dfd-4242-b481-f6807eb9adf7",
|
||||
"ApplicationListAll": "a61169be-a009-47ec-8658-3dd388af5c3e",
|
||||
"ApplicationListAvailable": "bf8d7986-2db7-4ff8-80c2-1935977730a6",
|
||||
"ApplicationListAppended": "ff7bde16-2631-4465-a4c5-349b357dd334",
|
||||
"ApplicationRegisterService": "c77a9f36-c007-4079-83fa-1c995b585a6f",
|
||||
"ApplicationUnRegisterService": "48460f25-fb1e-477f-b641-d5eeacce5e7a",
|
||||
"ApplicationCreate": "a3ec9f67-12a2-4e8a-b977-1acfa0069c12",
|
||||
"ApplicationUpdate": "83281757-696a-41ed-9706-e145ac54c3a9",
|
||||
"ApplicationBindEmployee": "80427237-5ab6-4d17-8084-cdb87bda22a3",
|
||||
"ApplicationBindOccupant": "ae0fb101-cb13-47ab-86bd-233a5dbef269",
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
||||
def get_routes() -> list[APIRouter]:
|
||||
return []
|
||||
|
||||
|
||||
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
|
||||
return [
|
||||
("/", "GET"),
|
||||
("/docs", "GET"),
|
||||
("/redoc", "GET"),
|
||||
("/openapi.json", "GET"),
|
||||
("/metrics", "GET"),
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
__all__ = []
|
||||
10
api_services/api_builds/management-service/events/index.py
Normal file
10
api_services/api_builds/management-service/events/index.py
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
events_index: dict = {
|
||||
"Slot1": "",
|
||||
"Slot2": "",
|
||||
"Slot3": "",
|
||||
"Slot4": "",
|
||||
"Slot5": "",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user