auth and token middleware context update

This commit is contained in:
2025-01-26 20:18:06 +03:00
parent 3d5a43220e
commit a7e48d8755
17 changed files with 265 additions and 345 deletions

View File

@@ -4,6 +4,7 @@ Authentication related API endpoints.
from typing import Union, Any, Dict
from ApiLayers.ApiValidations.Custom.wrapper_contexts import AuthContext, EventContext
from ApiLayers.Middleware import MiddlewareModule, TokenEventMiddleware
from ApiLayers.ApiValidations.Request import EmployeeSelection, OccupantSelection
@@ -39,7 +40,7 @@ AuthenticationLoginEventMethods = MethodToEvent(
},
headers=[],
errors=[],
url="/authentication/login",
url="/login",
method="POST",
summary="Login via domain and access key : [email] | [phone]",
description="Login to the system via domain, access key : [email] | [phone]",
@@ -49,9 +50,7 @@ def authentication_login_with_domain_and_creds(
request: Request,
data: EndpointBaseRequestModel,
) -> Dict[str, Any]:
function = AuthenticationLoginEventMethods.retrieve_event(
event_function_code=f"{authentication_login_super_user_event.key}"
)
function = AuthenticationLoginEventMethods.retrieve_event(event_function_code=f"{authentication_login_super_user_event.key}")
return function.endpoint_callable(request=request, data=data)
@@ -65,7 +64,7 @@ AuthenticationSelectEventMethods = MethodToEvent(
decorators_list=[MiddlewareModule.auth_required],
headers=[],
errors=[],
url="/authentication/select",
url="/select",
method="POST",
summary="Select company or occupant type",
description="Select company or occupant type",
@@ -95,7 +94,7 @@ AuthenticationCheckTokenEventMethods = MethodToEvent(
headers=[],
errors=[],
decorators_list=[MiddlewareModule.auth_required],
url="/authentication/check-token",
url="/check-token",
method="POST",
summary="Check if token is valid",
description="Check if access token is valid for user",
@@ -118,7 +117,7 @@ AuthenticationRefreshEventMethods = MethodToEvent(
headers=[],
errors=[],
decorators_list=[MiddlewareModule.auth_required],
url="/authentication/refresh",
url="/refresh",
method="POST",
summary="Refresh user info",
description="Refresh user info using access token",
@@ -143,7 +142,7 @@ AuthenticationChangePasswordEventMethods = MethodToEvent(
headers=[],
errors=[],
decorators_list=[MiddlewareModule.auth_required],
url="/authentication/change-password",
url="/change-password",
method="POST",
summary="Change password",
description="Change password with access token",
@@ -166,7 +165,7 @@ AuthenticationCreatePasswordEventMethods = MethodToEvent(
},
headers=[],
errors=[],
url="/authentication/create-password",
url="/create-password",
method="POST",
summary="Create password",
description="Create password with password reset token requested via email",
@@ -189,7 +188,7 @@ AuthenticationDisconnectUserEventMethods = MethodToEvent(
decorators_list=[MiddlewareModule.auth_required],
headers=[],
errors=[],
url="/authentication/disconnect",
url="/disconnect",
method="POST",
summary="Disconnect all sessions",
description="Disconnect all sessions of user in access token",
@@ -210,28 +209,25 @@ AuthenticationLogoutEventMethods = MethodToEvent(
},
headers=[],
errors=[],
url="/authentication/logout",
decorators_list=[TokenEventMiddleware.event_required],
url="/logout",
method="POST",
summary="Logout user",
description="Logout only single session of user which domain is provided",
)
@TokenEventMiddleware.event_required
def authentication_logout_user(request: Request, data: EndpointBaseRequestModel):
function = AuthenticationLogoutEventMethods.retrieve_event(
event_function_code=f"{authentication_logout_user_event.key}"
)
print('authentication_logout_user', dict(
auth=getattr(authentication_logout_user, "auth", None),
func_code=getattr(authentication_logout_user, "func_code", None),
))
function.endpoint_callable.auth = getattr(authentication_logout_user, "auth", None)
function.endpoint_callable.func_code = getattr(authentication_logout_user, "func_code", None)
event_context: EventContext = getattr(authentication_logout_user, "event_context", None)
print('event_context', event_context)
function = AuthenticationLogoutEventMethods.retrieve_event(event_function_code=f"{event_context.code}")
function.endpoint_callable.event_context = event_context
return function.endpoint_callable(request=request, data=data)
AuthenticationLogoutEventMethods.endpoint_callable = authentication_logout_user
AuthenticationRefreshTokenEventMethods = MethodToEvent(
name="AuthenticationRefreshTokenEventMethods",
events={
@@ -239,19 +235,22 @@ AuthenticationRefreshTokenEventMethods = MethodToEvent(
},
headers=[],
errors=[],
decorators_list=[MiddlewareModule.auth_required, ],
url="/authentication/refresh-token",
decorators_list=[MiddlewareModule.auth_required],
url="/refresh-token",
method="POST",
summary="Refresh token",
description="Refresh access token with refresher token",
)
def authentication_refresher_token(request: Request, data: EndpointBaseRequestModel):
token_dict = authentication_refresher_token.auth
auth_context: AuthContext = getattr(authentication_refresher_token, "auth_context", None)
function = AuthenticationRefreshTokenEventMethods.retrieve_event(
event_function_code=f"{authentication_refresher_token_event.key}"
)
return function.endpoint_callable(data=data, request=request, token_dict=token_dict)
function.endpoint_callable.auth_context = auth_context
return function.endpoint_callable(data=data, request=request)
AuthenticationRefreshTokenEventMethods.endpoint_callable = authentication_refresher_token
@@ -263,7 +262,7 @@ AuthenticationForgotPasswordEventMethods = MethodToEvent(
},
headers=[],
errors=[],
url="/authentication/forgot-password",
url="/forgot-password",
method="POST",
summary="Request password reset",
description="Send an email to user for a valid password reset token",
@@ -289,7 +288,7 @@ AuthenticationResetPasswordEventMethods = MethodToEvent(
headers=[],
errors=[],
decorators_list=[MiddlewareModule.auth_required],
url="/authentication/reset-password",
url="/reset-password",
method="POST",
summary="Reset password",
description="Reset user password",
@@ -314,7 +313,7 @@ AuthenticationDownloadAvatarEventMethods = MethodToEvent(
headers=[],
errors=[],
decorators_list=[],
url="/authentication/download-avatar",
url="/download-avatar",
method="POST",
summary="Download avatar",
description="Download avatar icon and profile info of user",

View File

@@ -23,20 +23,20 @@ AuthCluster = CategoryCluster(
prefix="/authentication",
description="Authentication cluster",
pageinfo=authentication_page_info,
endpoints=[
AuthenticationLoginEventMethods,
AuthenticationLogoutEventMethods,
AuthenticationRefreshTokenEventMethods,
AuthenticationForgotPasswordEventMethods,
AuthenticationChangePasswordEventMethods,
AuthenticationCheckTokenEventMethods,
AuthenticationCreatePasswordEventMethods,
AuthenticationDisconnectUserEventMethods,
AuthenticationDownloadAvatarEventMethods,
AuthenticationResetPasswordEventMethods,
AuthenticationRefreshEventMethods,
AuthenticationSelectEventMethods,
],
endpoints={
"AuthenticationLoginEventMethods": AuthenticationLoginEventMethods,
"AuthenticationLogoutEventMethods": AuthenticationLogoutEventMethods,
"AuthenticationRefreshTokenEventMethods": AuthenticationRefreshTokenEventMethods,
"AuthenticationForgotPasswordEventMethods": AuthenticationForgotPasswordEventMethods,
"AuthenticationChangePasswordEventMethods": AuthenticationChangePasswordEventMethods,
"AuthenticationCheckTokenEventMethods": AuthenticationCheckTokenEventMethods,
"AuthenticationCreatePasswordEventMethods": AuthenticationCreatePasswordEventMethods,
"AuthenticationDisconnectUserEventMethods": AuthenticationDisconnectUserEventMethods,
"AuthenticationDownloadAvatarEventMethods": AuthenticationDownloadAvatarEventMethods,
"AuthenticationResetPasswordEventMethods": AuthenticationResetPasswordEventMethods,
"AuthenticationRefreshEventMethods": AuthenticationRefreshEventMethods,
"AuthenticationSelectEventMethods": AuthenticationSelectEventMethods,
},
include_in_schema=True,
sub_category=[],
)

View File

@@ -5,6 +5,7 @@ from ApiLayers.ApiLibrary.common.line_number import get_line_number_for_error
from ApiLayers.ApiServices.Login.user_login_handler import UserLoginModule
from ApiLayers.ApiServices.Token.token_handler import TokenService
from ApiLayers.ApiValidations.Custom.token_objects import CompanyToken, OccupantToken
from ApiLayers.ApiValidations.Custom.wrapper_contexts import AuthContext, EventContext
from ApiLayers.ErrorHandlers import HTTPExceptionApi
from ApiLayers.Schemas import (
BuildLivingSpace,
@@ -372,14 +373,12 @@ def authentication_logout_user(request: Request, data: Any):
# 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")
token_dict = authentication_logout_user.auth
print('token_dict', token_dict)
func_code = authentication_logout_user.func_code
print('func_code', func_code)
return
event_context: EventContext = getattr(authentication_logout_user, "event_context", None)
return event_context.model_dump()
def authentication_refresher_token(request: Request, token_dict: TokenDictType, data: Any):
def authentication_refresher_token(request: Request, data: Any):
"""Refresh access token with refresher token"""
# token_refresher = UsersTokens.filter_by_one(
# token=data.refresh_token,
@@ -402,7 +401,8 @@ def authentication_refresher_token(request: Request, token_dict: TokenDictType,
# }
# return ResponseHandler.success("User is logged in successfully via refresher token", data=response_data)
# return ResponseHandler.not_found("Invalid data")
return
auth_context: AuthContext = getattr(authentication_refresher_token, "auth_context", None)
return auth_context.model_dump()
def authentication_forgot_password(request: Request, data: Any):

View File

@@ -4,3 +4,10 @@ import Events.AllEvents.validations as validations_events
events_list = (auths_events, events_events, validations_events)
def retrieve_cluster_by_name(cluster_name: str):
for module in events_list:
if hasattr(module, cluster_name, None):
return getattr(module, cluster_name, None)

View File

@@ -135,11 +135,11 @@ class MethodToEvent:
return found_event
raise ValueError(f"Event with function code {event_function_code} not found")
def retrieve_redis_value(self, cluster_name: str) -> Dict:
def retrieve_redis_value(self, cluster: "CategoryCluster") -> Dict:
"""
Key("METHOD_FUNCTION_CODES:{ClusterToMethod}:MethodEvent:Endpoint") : Value([FUNCTION_CODE, ...])
"""
redis_key = f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}:{cluster_name}:{self.name}:{self.URL}"
redis_key = f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}:{cluster.name}:{self.name}:{f"{cluster.PREFIX}{self.URL}"}"
return {redis_key: self.retrieve_all_event_keys()}
@staticmethod
@@ -156,8 +156,8 @@ class CategoryCluster:
PREFIX: str
PAGEINFO: PageInfo
DESCRIPTION: str
ENDPOINTS: list[MethodToEvent] # [MethodToEvent, ...]
SUBCATEGORY: Optional[List["CategoryCluster"]] # [CategoryCluster, ...]
ENDPOINTS: dict[str, MethodToEvent] # {"MethodToEvent": MethodToEvent, ...}
SUBCATEGORY: Optional[List["CategoryCluster"]] # [CategoryCluster, ...]
INCLUDE_IN_SCHEMA: Optional[bool] = True
def __init__(
@@ -167,7 +167,7 @@ class CategoryCluster:
prefix: str,
description: str,
pageinfo: PageInfo,
endpoints: list[MethodToEvent],
endpoints: dict[str, MethodToEvent],
sub_category: list,
include_in_schema: Optional[bool] = True,
):
@@ -176,7 +176,7 @@ class CategoryCluster:
self.PREFIX = prefix
self.PAGEINFO = pageinfo
self.DESCRIPTION = description
self.ENDPOINTS = endpoints or []
self.ENDPOINTS = endpoints or {}
self.SUBCATEGORY = sub_category or []
self.INCLUDE_IN_SCHEMA = include_in_schema
@@ -189,7 +189,7 @@ class CategoryCluster:
RedisCategoryKeys.CLUSTER_2_METHOD_EVENT
Returns the class name and function codes for the class.
"""
dict_cluster_2_method, list_endpoints = {}, [i.name for i in self.ENDPOINTS]
dict_cluster_2_method, list_endpoints = {}, [i.name for i in self.ENDPOINTS.values()]
for endpoint_name in list_endpoints:
dict_cluster_2_method[endpoint_name] = self.name
dict_cluster_2_method[self.name] = list_endpoints
@@ -200,7 +200,7 @@ class CategoryCluster:
Retrieves all function codes by iterating over the events list.
"""
all_function_codes = []
for event_method in self.ENDPOINTS:
for event_method in self.ENDPOINTS.values():
all_function_codes.extend([str(event_key) for event_key in event_method.EVENTS.keys()])
return all_function_codes

View File

@@ -1,4 +1,5 @@
from Events.AllEvents.events_file import events_list
from .category_cluster_models import cluster_controller
@@ -7,10 +8,12 @@ def get_cluster_controller_group():
cluster_controller.import_all_category_clusters(cluster)
return cluster_controller
"""
prepare_routing = PrepareRouting(cluster_controller_group=cluster_controller)
prepare_events = PrepareEvents(cluster_controller_group=cluster_controller)
set_items_2_redis = SetItems2Redis(prepare_events=prepare_events)
print(set_items_2_redis)
print(prepare_routing)
"""

View File

@@ -38,7 +38,7 @@ class PrepareRouting(DecoratorModule):
return self.__safe_endpoint_list
def create_endpoints(self, cluster: CategoryCluster, created_router):
for method_endpoint in list(cluster.ENDPOINTS):
for method_endpoint in list(cluster.ENDPOINTS.values()):
# Filter out the original function and apply decorators
applied_decorators_qualname = self.apply_decorators(method_endpoint)
# Register the endpoint with FastAPI router
@@ -89,17 +89,17 @@ class PrepareEvents(DecoratorModule):
f"{self.valid_redis_items.CLUSTER_FUNCTION_CODES_KEY}:{cluster.name}" : tuple(cluster.retrieve_all_function_codes())
}
for method_endpoint in list(cluster.ENDPOINTS):
for method_endpoint in list(cluster.ENDPOINTS.values()):
# [SAVE]REDIS => ENDPOINT2CLASS = {MethodEvent: Endpoint("/.../.../..."), ...}
self.valid_redis_items.ENDPOINT2CLASS_VALUE.update(
{f"{cluster.name}:{method_endpoint.name}": method_endpoint.URL}
{f"{cluster.name}:{method_endpoint.name}": f"{cluster.PREFIX}{method_endpoint.URL}"}
)
self.valid_redis_items.ENDPOINT2CLASS_VALUE.update(
{method_endpoint.URL :f"{cluster.name}:{method_endpoint.name}"}
{f"{cluster.PREFIX}{method_endpoint.URL}" :f"{cluster.name}:{method_endpoint.name}"}
)
# [SAVE]REDIS => METHOD_FUNCTION_CODES:MethodEvent:Endpoint = [FUNCTION_CODE, ...]
self.valid_redis_items.METHOD_FUNCTION_CODES_VALUE.update(
method_endpoint.retrieve_redis_value(cluster_name=cluster.name)
method_endpoint.retrieve_redis_value(cluster=cluster)
)
@@ -112,28 +112,22 @@ class SetItems2Redis:
self.set_items()
def __str__(self):
self.std_out = f"\nSetItems2Redis:\n\n{self.std_out}"
return self.std_out
return f"\nSetItems2Redis:\n\n{self.std_out}"
def set_items(self):
from ApiLayers.AllConfigs.Redis.configs import RedisCategoryKeys
dict_prep = self.prepare_events.valid_redis_items.as_dict
RedisActions.delete(
list_keys=[
f"{RedisCategoryKeys.MENU_FIRST_LAYER}:*",
f"{RedisCategoryKeys.CLUSTER_INDEX}:*",
f"{RedisCategoryKeys.CLUSTER_FUNCTION_CODES}:*",
f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}:*"
f"{RedisCategoryKeys.ENDPOINT2CLASS}:*",
]
)
for redis_values_to_delete, redis_key_type in RedisCategoryKeys.__annotations__.items():
if isinstance(redis_key_type, str):
continue
RedisActions.delete(list_keys=[f"{redis_values_to_delete}*"])
# Save MENU_FIRST_LAYER to Redis
redis_list = RedisList(redis_key=RedisCategoryKeys.MENU_FIRST_LAYER)
RedisActions.set_json(
list_keys=redis_list.to_list(), value=dict_prep.get(RedisCategoryKeys.MENU_FIRST_LAYER)
)
self.std_out = f"{RedisCategoryKeys.MENU_FIRST_LAYER}: {dict_prep.get(RedisCategoryKeys.MENU_FIRST_LAYER)}\n"
self.std_out += f"{RedisCategoryKeys.MENU_FIRST_LAYER}: {dict_prep.get(RedisCategoryKeys.MENU_FIRST_LAYER)}\n"
# Save CLUSTER_INDEX to Redis
redis_list = RedisList(redis_key=RedisCategoryKeys.CLUSTER_INDEX)
@@ -167,5 +161,11 @@ class SetItems2Redis:
self.std_out += f"\n{RedisCategoryKeys.ENDPOINT2CLASS}: {dict_prep.get(RedisCategoryKeys.ENDPOINT2CLASS)}\n"
RedisActions.set_json(
list_keys=[f"{RedisCategoryKeys.REBUILD}:*"], value=False
list_keys=[f"{RedisCategoryKeys.REBUILD}:*"], value={
f"{RedisCategoryKeys.MENU_FIRST_LAYER}": True,
f"{RedisCategoryKeys.CLUSTER_INDEX}": True,
f"{RedisCategoryKeys.CLUSTER_FUNCTION_CODES}": True,
f"{RedisCategoryKeys.METHOD_FUNCTION_CODES}": True,
f"{RedisCategoryKeys.ENDPOINT2CLASS}": True,
}
)