diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 92122e5..1738102 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -6,18 +6,16 @@
-
-
-
-
+
-
+
+
-
-
+
+
@@ -42,14 +40,14 @@
- {
+ "keyToString": {
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "git-widget-placeholder": "development",
+ "last_opened_file_path": "/home/berkay/git-gitea-evyos/wag-managment-api-service-version-5/ApiLayers/LanguageModels/templates"
}
-}]]>
+}
diff --git a/ApiLayers/ApiServices/Token/token_handler.py b/ApiLayers/ApiServices/Token/token_handler.py
index e10bfc2..362ab0b 100644
--- a/ApiLayers/ApiServices/Token/token_handler.py
+++ b/ApiLayers/ApiServices/Token/token_handler.py
@@ -2,6 +2,8 @@
from typing import List, Union, TypeVar, Dict, Any, TYPE_CHECKING
+import arrow
+
from ApiLayers.AllConfigs.Token.config import Auth
from ApiLayers.ApiLibrary.common.line_number import get_line_number_for_error
from ApiLayers.ApiLibrary.date_time_actions.date_functions import DateTimeLocal
@@ -51,11 +53,7 @@ class TokenService:
@classmethod
def _get_user_tokens(cls, user: Users) -> RedisResponse:
"""Get all tokens for a user from Redis."""
- return RedisActions.get_json(
- list_keys=AccessToken(
- userUUID=user.uu_id,
- ).to_list()
- )
+ return RedisActions.get_json(list_keys=[f"*:{str(user.uu_id)}"])
@classmethod
def do_occupant_login(
@@ -147,6 +145,7 @@ class TokenService:
userUUID=user.uu_id,
accessToken=cls._create_access_token(),
)
+ cls.remove_token_with_domain(user=user, domain=model.get("domain"))
redis_action = RedisActions.set_json(
list_keys=access_object.to_list(),
value=model,
@@ -282,8 +281,9 @@ class TokenService:
remember: bool,
) -> Dict[str, Any]:
"""Set access token to redis and handle user session."""
+ from ApiLayers.AllConfigs.Token.config import Auth
cls.remove_token_with_domain(user=user, domain=domain)
- Users.client_arrow = DateTimeLocal(is_client=True, timezone=user.local_timezone)
+ # Users.client_arrow = DateTimeLocal(is_client=True, timezone=user.local_timezone)
login_dict, db_session = {}, UsersTokens.new_session()
if user.is_occupant: # Handle login based on user type
login_dict = cls.do_occupant_login(
@@ -295,30 +295,35 @@ class TokenService:
)
# Handle remember me functionality
+ user.remember_me = bool(remember)
if remember:
+ users_token_created = cls._create_access_token(access=False)
+ login_dict["refresh_token"] = users_token_created
users_token = UsersTokens.find_or_create(
db=db_session,
user_id=user.id,
token_type="RememberMe",
- token=cls._create_access_token(access=False),
domain=domain,
)
- if users_token.meta_data.get("created"):
- user.remember_me = True
+ if users_token.meta_data.created:
+ users_token.token = users_token_created
+ users_token.save(db=db_session)
+ else:
+ if arrow.now() > arrow.get(str(users_token.expires_at)): # Check if token is expired
+ users_token.token = users_token_created
+ users_token.expires_at = str(arrow.now().datetime + Auth.TOKEN_EXPIRE_DAY_1)
+ users_token.save(db=db_session)
+ else:
+ login_dict["refresh_token"] = users_token.token
else:
- if UsersTokens.filter_all(
+ already_refresher = UsersTokens.filter_all(
UsersTokens.user_id == user.id,
UsersTokens.token_type == "RememberMe",
UsersTokens.domain == domain,
db=db_session,
- ).data:
- UsersTokens.filter_all(
- UsersTokens.user_id == user.id,
- UsersTokens.token_type == "RememberMe",
- UsersTokens.domain == domain,
- db=db_session,
- ).query.delete(synchronize_session=False)
- user.remember_me = False
+ )
+ if already_refresher.count:
+ already_refresher.query.delete(synchronize_session=False)
user.save(db=db_session)
return {**login_dict, "user": user.get_dict()}
@@ -399,7 +404,6 @@ class TokenService:
return EmployeeTokenObject(**redis_object)
elif redis_object.get("user_type") == UserType.occupant.value:
return OccupantTokenObject(**redis_object)
-
raise HTTPExceptionApi(
error_code="",
lang="en",
diff --git a/ApiLayers/LanguageModels/Response/authentication/auth.py b/ApiLayers/LanguageModels/Response/authentication/auth.py
index 067ea6f..8edef2a 100644
--- a/ApiLayers/LanguageModels/Response/authentication/auth.py
+++ b/ApiLayers/LanguageModels/Response/authentication/auth.py
@@ -49,12 +49,20 @@ authResponses = {
"message": "Password changed successfully.",
},
},
- "DISCONNECTED_USER": {
+ "LOGOUT_USER": {
"tr": {
"message": "Kullanıcı başarılı bir şekilde çıkış yaptı.",
},
"en": {
- "message": "User logged out successfully.",
+ "message": "User successfully logged out.",
+ },
+ },
+ "DISCONNECTED_USER": {
+ "tr": {
+ "message": "Kullanıcı tüm cihazlardan başarılı bir şekilde çıkış yaptı.",
+ },
+ "en": {
+ "message": "User successfully logged out of all devices.",
},
},
"USER_NOT_FOUND": {
@@ -72,5 +80,21 @@ authResponses = {
"en": {
"message": "Password reset request created successfully.",
},
+ },
+ "USER_AVATAR": {
+ "tr": {
+ "message": "Kullanıcı avatarı data blogunda belirtildiği şekildedir.",
+ },
+ "en": {
+ "message": "User avatar is as specified in the data block.",
+ },
+ },
+ "TOKEN_REFRESH": {
+ "tr": {
+ "message": "Token başarılı bir şekilde yenilendi.",
+ },
+ "en": {
+ "message": "Token successfully refreshed.",
+ },
}
}
diff --git a/Events/AllEvents/authentication/auth/api_events.py b/Events/AllEvents/authentication/auth/api_events.py
index 53e32ab..6e9d44d 100644
--- a/Events/AllEvents/authentication/auth/api_events.py
+++ b/Events/AllEvents/authentication/auth/api_events.py
@@ -124,7 +124,7 @@ authentication_disconnect_user_event.endpoint_callable = (
authentication_logout_user_event = Event(
name="authentication_logout_user_event",
key="g1j8i6j7-9k4h-0h6l-4i3j-2j0k1k0j0i0k",
- request_validator=None, # TODO: Add request validator
+ request_validator=AuthenticationRequestModels.LogoutRequestModel,
language_models=[],
# response_validator=None, # TODO: Add response validator
description="Logout user session",
@@ -140,9 +140,9 @@ authentication_logout_user_event.endpoint_callable = (
authentication_refresher_token_event = Event(
name="authentication_refresher_token_event",
key="h2k9j7k8-0l5i-1i7m-5j4k-3k1l2l1k1j1l",
- request_validator=None, # TODO: Add request validator
+ request_validator=AuthenticationRequestModels.RefresherRequestModel, # TODO: Add request validator
language_models=[],
- # response_validator=None, # TODO: Add response validator
+ # response_validator=None,
description="Refresh authentication token",
)
diff --git a/Events/AllEvents/authentication/auth/auth.py b/Events/AllEvents/authentication/auth/auth.py
index b103f66..dc75e66 100644
--- a/Events/AllEvents/authentication/auth/auth.py
+++ b/Events/AllEvents/authentication/auth/auth.py
@@ -113,7 +113,7 @@ AuthenticationCheckTokenEventMethods = MethodToEvent(
)
-def authentication_check_token_is_valid():
+def authentication_check_token_is_valid(request: Request):
context_retriever = ContextRetrievers(func=authentication_check_token_is_valid)
function = AuthenticationCheckTokenEventMethods.retrieve_event(
event_function_code=f"{authentication_check_token_event.key}"
@@ -142,7 +142,7 @@ AuthenticationRefreshEventMethods = MethodToEvent(
)
-def authentication_refresh_user_info():
+def authentication_refresh_user_info(request: Request):
context_retriever = ContextRetrievers(func=authentication_refresh_user_info)
function = AuthenticationRefreshEventMethods.retrieve_event(
event_function_code=f"{authentication_refresh_user_info_event.key}"
@@ -169,7 +169,7 @@ AuthenticationChangePasswordEventMethods = MethodToEvent(
)
-def authentication_change_password_event_callable(data: EndpointBaseRequestModel):
+def authentication_change_password_event_callable(request: Request, data: EndpointBaseRequestModel):
context_retriever = ContextRetrievers(
func=authentication_change_password_event_callable
)
@@ -198,7 +198,7 @@ AuthenticationCreatePasswordEventMethods = MethodToEvent(
)
-def authentication_create_password(data: EndpointBaseRequestModel):
+def authentication_create_password(request: Request, data: EndpointBaseRequestModel):
context_retriever = ContextRetrievers(func=authentication_create_password)
function = AuthenticationCreatePasswordEventMethods.retrieve_event(
event_function_code=f"{authentication_create_password_event.key}"
@@ -227,13 +227,18 @@ AuthenticationDisconnectUserEventMethods = MethodToEvent(
)
-def authentication_disconnect_user(data: EndpointBaseRequestModel):
+def authentication_disconnect_user(request: Request):
context_retriever = ContextRetrievers(func=authentication_disconnect_user)
function = AuthenticationDisconnectUserEventMethods.retrieve_event(
event_function_code=f"{authentication_disconnect_user_event.key}"
)
AuthenticationFunctions.context_retriever = context_retriever
- return function.endpoint_callable(data=data)
+ return function.endpoint_callable()
+
+
+AuthenticationDisconnectUserEventMethods.endpoint_callable = (
+ authentication_disconnect_user
+)
AuthenticationLogoutEventMethods = MethodToEvent(
@@ -249,13 +254,14 @@ AuthenticationLogoutEventMethods = MethodToEvent(
)
-def authentication_logout_user(data: EndpointBaseRequestModel):
+def authentication_logout_user(request: Request, data: EndpointBaseRequestModel):
context_retriever = ContextRetrievers(func=authentication_logout_user)
function = AuthenticationLogoutEventMethods.retrieve_event(
event_function_code=f"{authentication_logout_user_event.key}"
)
+ validated_data = function.REQUEST_VALIDATOR(**data.data)
AuthenticationFunctions.context_retriever = context_retriever
- return function.endpoint_callable(data=data)
+ return function.endpoint_callable(data=validated_data)
AuthenticationLogoutEventMethods.endpoint_callable = authentication_logout_user
@@ -268,7 +274,7 @@ AuthenticationRefreshTokenEventMethods = MethodToEvent(
},
headers=[],
errors=[],
- decorators_list=[MiddlewareModule.auth_required],
+ decorators_list=[],
url="/refresh-token",
method="POST",
summary="Refresh token",
@@ -276,13 +282,12 @@ AuthenticationRefreshTokenEventMethods = MethodToEvent(
)
-def authentication_refresher_token(data: EndpointBaseRequestModel):
- context_retriever = ContextRetrievers(func=authentication_refresher_token)
+def authentication_refresher_token(request: Request, data: EndpointBaseRequestModel):
function = AuthenticationRefreshTokenEventMethods.retrieve_event(
event_function_code=f"{authentication_refresher_token_event.key}"
)
- AuthenticationFunctions.context_retriever = context_retriever
- return function.endpoint_callable(data=data)
+ validated_data = function.REQUEST_VALIDATOR(**data.data)
+ return function.endpoint_callable(request=request, data=validated_data)
AuthenticationRefreshTokenEventMethods.endpoint_callable = (
@@ -304,7 +309,7 @@ AuthenticationForgotPasswordEventMethods = MethodToEvent(
)
-def authentication_forgot_password(data: EndpointBaseRequestModel):
+def authentication_forgot_password(request: Request, data: EndpointBaseRequestModel):
context_retriever = ContextRetrievers(func=authentication_forgot_password)
function = AuthenticationForgotPasswordEventMethods.retrieve_event(
event_function_code=f"{authentication_forgot_password_event.key}"
@@ -333,7 +338,7 @@ AuthenticationResetPasswordEventMethods = MethodToEvent(
)
-def authentication_reset_password(data: EndpointBaseRequestModel):
+def authentication_reset_password(request: Request, data: EndpointBaseRequestModel):
context_retriever = ContextRetrievers(func=authentication_reset_password)
function = AuthenticationResetPasswordEventMethods.retrieve_event(
event_function_code=f"{authentication_reset_password_event.key}"
@@ -362,7 +367,7 @@ AuthenticationDownloadAvatarEventMethods = MethodToEvent(
)
-def authentication_download_avatar():
+def authentication_download_avatar(request: Request):
context_retriever = ContextRetrievers(func=authentication_download_avatar)
function = AuthenticationDownloadAvatarEventMethods.retrieve_event(
event_function_code=f"{authentication_download_avatar_event.key}"
diff --git a/Events/AllEvents/authentication/auth/function_handlers.py b/Events/AllEvents/authentication/auth/function_handlers.py
index d825e6e..386ae6b 100644
--- a/Events/AllEvents/authentication/auth/function_handlers.py
+++ b/Events/AllEvents/authentication/auth/function_handlers.py
@@ -27,7 +27,9 @@ from ApiLayers.Schemas import (
Users,
UsersTokens,
)
-from Events.base_request_model import ContextRetrievers, TokenDictType
+from Events.base_request_model import TokenDictType, BaseRouteModel
+from Services.Redis.Actions.actions import RedisActions
+from ApiLayers.AllConfigs.Redis.configs import RedisAuthKeys
class Handlers:
@@ -211,11 +213,9 @@ class Handlers:
)
-class AuthenticationFunctions:
+class AuthenticationFunctions(BaseRouteModel):
"""Class for handling authentication functions"""
- context_retriever: Union[ContextRetrievers] = None
-
@classmethod # Requires no auth context
def authentication_login_with_domain_and_creds(cls, request: Request, data: Any):
"""
@@ -292,13 +292,13 @@ class AuthenticationFunctions:
return EndpointSuccessResponse(
code="USER_INFO_REFRESHED", lang=cls.context_retriever.token.lang
).as_dict({
- "access_token": cls.context_retriever.token, "user": found_user.get_dict(),
+ "access_token": cls.context_retriever.get_token, "user": found_user.get_dict(),
})
if not found_user:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(
- data={"user": found_user.get_dict()}
+ data={}
)
@classmethod # Requires no auth context
@@ -315,7 +315,7 @@ class AuthenticationFunctions:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(
- data={"user": found_user.get_dict()}
+ data={}
)
@classmethod # Requires not auth context
@@ -349,14 +349,18 @@ class AuthenticationFunctions:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(
- data={"user": found_user.get_dict()}
+ data={}
)
registered_tokens = UsersTokens.filter_all(
- UsersTokens.user_id == cls.context_retriever.token.id, db=db
+ UsersTokens.user_id == cls.context_retriever.token.user_id, db=db
)
if registered_tokens.count:
registered_tokens.query.delete()
UsersTokens.save(db=db)
+
+ RedisActions.delete(
+ list_keys=[f"{RedisAuthKeys.AUTH}:*:{str(found_user.uu_id)}"]
+ )
return EndpointSuccessResponse(
code="DISCONNECTED_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
@@ -372,23 +376,32 @@ class AuthenticationFunctions:
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
).as_dict(
- data={"user": found_user.get_dict()}
+ data={}
)
registered_tokens = UsersTokens.filter_all_system(
- UsersTokens.user_id == cls.context_retriever.token.id,
+ UsersTokens.user_id == cls.context_retriever.token.user_id,
UsersTokens.domain == cls.context_retriever.token.domain,
db=db,
)
if registered_tokens.count:
registered_tokens.query.delete()
UsersTokens.save(db=db)
+ TokenService.remove_token_with_domain(user=found_user, domain=data.domain)
return EndpointSuccessResponse(
- code="DISCONNECTED_USER", lang=cls.context_retriever.token.lang
+ code="LOGOUT_USER", lang=cls.context_retriever.token.lang
).as_dict(data={"user": found_user.get_dict()})
@classmethod # Requires not auth context
- def authentication_refresher_token(cls, data: Any):
- """Refresh access token with refresher token"""
+ def authentication_refresher_token(cls, request: Request, data: Any):
+ """
+ Refresh access token with refresher token
+ {
+ "data": {
+ "refresh_token": "string",
+ "domain": "string"
+ }
+ }
+ """
import arrow
from ApiLayers.ApiServices.Token.token_handler import TokenService
db = UsersTokens.new_session()
@@ -397,15 +410,15 @@ class AuthenticationFunctions:
domain=data.domain,
db=db,
).data
+ language = request.headers.get("evyos-language", "tr")
if not token_refresher:
return EndpointNotAcceptableResponse(
- code="REFRESHER_NOT_FOUND", lang=cls.context_retriever.token.lang
+ code="REFRESHER_NOT_FOUND", lang=language
).as_dict(
data={"refresh_token": data.refresh_token}
)
if found_user := Users.filter_one(Users.id == token_refresher.user_id, db=db).data:
- request = cls.context_retriever.request
token_created = TokenService.set_access_token_to_redis(
request=request,
user=found_user,
@@ -420,9 +433,12 @@ class AuthenticationFunctions:
"access_token": token_created.get("access_token"),
"refresh_token": data.refresh_token,
}
- return EndpointSuccessResponse(
- code="TOKEN_REFRESH", lang=cls.context_retriever.token.lang
- ).as_dict(data=response_data)
+ return EndpointSuccessResponse(code="TOKEN_REFRESH", lang=language).as_dict(data=response_data)
+ raise EndpointNotAcceptableResponse(
+ code="USER_NOT_FOUND", lang=language
+ ).as_dict(
+ data={}
+ )
@classmethod # Requires not auth context
def authentication_forgot_password(cls, data: Any):
@@ -463,7 +479,7 @@ class AuthenticationFunctions:
return cls.context_retriever.base
@classmethod # Requires not auth context
- def authentication_download_avatar(cls, data: Any):
+ def authentication_download_avatar(cls):
"""Download avatar icon and profile info of user"""
import arrow
db = Users.new_session()
@@ -471,19 +487,19 @@ class AuthenticationFunctions:
Users.id == cls.context_retriever.token.user_id, db=db
).data:
expired_starts = str(arrow.now() - arrow.get(str(found_user.expiry_ends)))
- expired_int = arrow.now() - arrow.get(str(found_user.expiry_ends)).days
+ expired_int = arrow.now().datetime - arrow.get(str(found_user.expiry_ends)).datetime
user_info = {
"lang": cls.context_retriever.token.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),
+ "expired_humanized": expired_starts,
+ "expired_day": int(expired_int.days) * -1,
}
return EndpointSuccessResponse(
code="USER_AVATAR", lang=cls.context_retriever.token.lang
).as_dict(data=user_info)
return EndpointNotAcceptableResponse(
code="USER_NOT_FOUND", lang=cls.context_retriever.token.lang
- ).as_dict(data={"user": found_user.get_dict()})
+ ).as_dict(data={})
diff --git a/Events/AllEvents/authentication/auth/info.py b/Events/AllEvents/authentication/auth/info.py
index dc58175..577eed9 100644
--- a/Events/AllEvents/authentication/auth/info.py
+++ b/Events/AllEvents/authentication/auth/info.py
@@ -7,5 +7,5 @@ authentication_page_info = PageInfo(
description={"en": "Authentication"},
icon="",
parent="",
- url="",
+ url=""
)
diff --git a/Events/AllEvents/authentication/auth/models.py b/Events/AllEvents/authentication/auth/models.py
index b763d59..4e041ef 100644
--- a/Events/AllEvents/authentication/auth/models.py
+++ b/Events/AllEvents/authentication/auth/models.py
@@ -2,9 +2,11 @@ from ApiLayers.ApiValidations.Request import (
Login,
EmployeeSelection,
OccupantSelection,
+ Logout,
CreatePassword,
ChangePassword,
Forgot,
+ Remember,
)
@@ -13,6 +15,8 @@ class AuthenticationRequestModels:
SelectCompanyOrOccupantTypeSuperUserRequestModel = {
"EmployeeSelection": EmployeeSelection, "OccupantSelection": OccupantSelection,
}
+ RefresherRequestModel = Remember
+ LogoutRequestModel = Logout
class AuthenticationResponseModels:
diff --git a/Events/Engine/abstract_class.py b/Events/Engine/abstract_class.py
index 7c8131f..f308ee8 100644
--- a/Events/Engine/abstract_class.py
+++ b/Events/Engine/abstract_class.py
@@ -11,6 +11,7 @@ class PageInfo:
BUTTON_NAME: str
PAGE_URL: str
PAGEINFO: Dict[str, Any]
+ URL: str = ""
def __init__(
self,
@@ -18,13 +19,15 @@ class PageInfo:
title: Dict[str, Any],
description: Dict[str, Any],
icon: str,
- parent: str
+ parent: str,
+ url: str,
):
self.NAME = name
self.TITLE = title
self.DESCRIPTION = description
self.ICON = icon
self.PARENT = parent
+ self.URL = url
class Event:
diff --git a/Events/base_request_model.py b/Events/base_request_model.py
index b77356d..dda04d8 100644
--- a/Events/base_request_model.py
+++ b/Events/base_request_model.py
@@ -13,11 +13,10 @@ from ApiLayers.ApiValidations.Custom.token_objects import (
OccupantTokenObject,
)
from ApiLayers.ApiValidations.Custom.wrapper_contexts import AuthContext, EventContext
+from ApiLayers.AllConfigs.Token.config import Auth
-TokenDictType = Union[
- EmployeeTokenObject, OccupantTokenObject
-] # Type aliases for common types
+TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject]
class EndpointBaseRequestModel(BaseModel):
@@ -81,3 +80,13 @@ class ContextRetrievers:
def base(self) -> Optional[dict[str, Any]]:
"""Retrieve base request model from a function."""
return getattr(self.context, "base", None)
+
+ @property
+ def get_token(self) -> Optional[str]:
+ """Retrieve access key from a function."""
+ return getattr(self.request, "headers", {}).get(Auth.ACCESS_TOKEN_TAG, None)
+
+
+class BaseRouteModel:
+
+ context_retriever: Union[ContextRetrievers] = None
diff --git a/Services/Redis/Models/access.py b/Services/Redis/Models/access.py
index c886b7a..1c55ee8 100644
--- a/Services/Redis/Models/access.py
+++ b/Services/Redis/Models/access.py
@@ -29,7 +29,7 @@ class AccessToken(BaseRedisModel):
@property
def count(self):
- return 2
+ return 3
@property
def delimiter(self):