Commit 86779f3c authored by arun.uday's avatar arun.uday

AssetManager-V1.0- Not reviewed

Updated the code with cookie and token generation, added the redis db for token storage in server side, added fields for google and microsoft login, added updation of login time.
parent 75ec7a5c
...@@ -5,6 +5,6 @@ class ApiEndPoints: ...@@ -5,6 +5,6 @@ class ApiEndPoints:
# common # common
asset_manager_submit: str = "/submit" asset_manager_submit: str = "/submit"
asset_manager_user_registration: str = "/register" asset_manager_user_registration: str = "/register"
# login-management # login-management
asset_manager_login: str = "/login" asset_manager_login: str = "/login"
...@@ -10,22 +10,28 @@ class LoginHandlers: ...@@ -10,22 +10,28 @@ class LoginHandlers:
self.obj_login_handler = NormalLogin() self.obj_login_handler = NormalLogin()
def normal_login(self, login_data, request): def normal_login(self, login_data, request):
# decrypting the password from the UI # decrypting the password from the UI
decrypted_password = self.obj_login_handler.password_decrypt(login_data.payload["password"]) decrypted_password = self.obj_login_handler.password_decrypt(login_data.payload["password"])
# validating the received inputs empty or not # validating the received inputs empty or not
response = self.obj_login_handler.user_data_validation(login_data.payload["username"], decrypted_password) response = self.obj_login_handler.user_data_validation(
login_data.payload["username"],
login_data.project_id,
decrypted_password)
# Account is not registered # Account is not registered
if response is not None: if response is not None:
return JSONResponse(content=DefaultFailureResponse(error=response).dict(), return JSONResponse(content=DefaultFailureResponse(error=response).dict(),
status_code=status.HTTP_400_BAD_REQUEST) status_code=status.HTTP_400_BAD_REQUEST)
# checking for the account and password matching # checking for the account and password matching
response, data = self.obj_login_handler.db_password_matching(login_data.project_id, response, data = self.obj_login_handler.db_password_matching(login_data.payload,
login_data.payload,
decrypted_password) decrypted_password)
# if the passwords doesn't match with the db data # if the passwords doesn't match with the db data
if response is not None: if response is not None:
return JSONResponse(content=DefaultFailureResponse(error=data).dict(), return JSONResponse(content=DefaultFailureResponse(error=data).dict(),
status_code=status.HTTP_401_UNAUTHORIZED) status_code=status.HTTP_401_UNAUTHORIZED)
# generating the access tokens # generating the access tokens
response = self.obj_login_handler.generate_cookie_tokens(login_data.payload, request) response = self.obj_login_handler.generate_cookie_tokens(login_data.payload, request)
# token generation unsuccessful # token generation unsuccessful
......
...@@ -7,9 +7,8 @@ from Cryptodome.Cipher import AES ...@@ -7,9 +7,8 @@ from Cryptodome.Cipher import AES
from passlib.context import CryptContext from passlib.context import CryptContext
from validate_email import validate_email from validate_email import validate_email
from scripts.config import * from scripts.config import Services, Secrets
from scripts.database.mongo.mongo_login import MongoUser from scripts.database.mongo.mongo_login import MongoUser
from scripts.database.redis.redis_conn import login_db
from scripts.errors import ErrorMessages from scripts.errors import ErrorMessages
from scripts.logging.logger import logger from scripts.logging.logger import logger
from scripts.utils.security.apply_encrytion_util import create_token from scripts.utils.security.apply_encrytion_util import create_token
...@@ -52,7 +51,7 @@ class NormalLogin: ...@@ -52,7 +51,7 @@ class NormalLogin:
logger.exception(e) logger.exception(e)
@staticmethod @staticmethod
def user_data_validation(username, password) -> dict | None: def user_data_validation(username, project_id, password) -> dict | None:
try: try:
# checking for valid username # checking for valid username
if username == "" or username == "user@example.com" or validate_email(username) is not True: if username == "" or username == "user@example.com" or validate_email(username) is not True:
...@@ -60,39 +59,47 @@ class NormalLogin: ...@@ -60,39 +59,47 @@ class NormalLogin:
# checking for valid password # checking for valid password
if password == "" or password == "string": if password == "" or password == "string":
return {"message": ErrorMessages.ERROR_INVALID_PASSWORD, "data": password} return {"message": ErrorMessages.ERROR_INVALID_PASSWORD, "data": password}
# check if the project id matches
if Services.PROJECT_ID != project_id:
return {"message": ErrorMessages.ERROR_INVALID_PROJECT_ID, "data": project_id}
return None return None
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
def db_data_validation(self, project_id, login_data): def db_data_validation(self, username):
try: try:
# fetching the data based on the username # fetching the data based on the username
self.db_user_data = MongoUser().fetch_user_details(login_data["username"]) self.db_user_data = MongoUser().fetch_user_details(username)
# if the user is not available # if the user is not available
if not self.db_user_data: if not self.db_user_data:
return False, {"message": ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN, return False, {"message": ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN,
"data": {"username": login_data["username"]}} "data": {"username": username}}
# if the user is not registered through normal login
if self.db_user_data["login_type"] != "normal":
return False, {"message": ErrorMessages.ERROR_LOGIN_TYPE_INVALID,
"data": {"username": username, "Use Login": self.db_user_data["login_type"]}}
# Check the project id from the request body # Check the project id from the request body
if self.db_user_data["project_id"] != Services.PROJECT_ID or Services.PROJECT_ID != project_id: if self.db_user_data["project_id"] != Services.PROJECT_ID:
return False, {"message": ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN, "data": login_data.username} return False, {"message": ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN, "data": username}
# if the user exist # if the user exist
return None, {"message": True} return None, {"message": True}
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
def db_password_matching(self, project_id, login_data, password): def db_password_matching(self, payload, password):
try: try:
# getting the response after checking for the user data in db # getting the response after checking for the user data in db
response, message = self.db_data_validation(project_id, login_data) response, message = self.db_data_validation(payload["username"])
# if the response is false then an error message is send back # if the response is false then an error message is send back
if response is not None: if response is not None:
return response, message return response, message
# if the user exists in db then password is matched # if the user exists in db then password is matched
if not self.pwd_context.verify(password, self.db_user_data["password"]): if not self.pwd_context.verify(password, self.db_user_data["password"]):
return False, {"message": ErrorMessages.ERROR_PASSWORD_MISMATCH, return False, {"message": ErrorMessages.ERROR_PASSWORD_MISMATCH,
"data": {"username": login_data["username"]}} "data": {"username": payload["username"]}}
# if the password is correct # if the password is correct
return None, {"username": login_data["username"], "role": self.db_user_data["user_role"]} return None, {"username": payload["username"], "role": self.db_user_data["user_role"]}
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
...@@ -105,6 +112,7 @@ class NormalLogin: ...@@ -105,6 +112,7 @@ class NormalLogin:
ip=request.ip_address, ip=request.ip_address,
project_id=Services.PROJECT_ID, project_id=Services.PROJECT_ID,
) )
# returning the login token
if access_token: if access_token:
return {"user_id": access_token, "token_type": "bearer"} return {"user_id": access_token, "token_type": "bearer"}
else: else:
......
from scripts.config import Databases from scripts.config import Databases
from scripts.utils.mongo_utils import MongoConnect from scripts.utils.mongo_utils import MongoConnect
# creating the mongo connection
mongo_obj = MongoConnect(uri=Databases.MONGO_URI) mongo_obj = MongoConnect(uri=Databases.MONGO_URI)
mongo_client = mongo_obj() mongo_client = mongo_obj()
CollectionBaseClass = mongo_obj.get_base_class() CollectionBaseClass = mongo_obj.get_base_class()
...@@ -27,12 +27,14 @@ class MongoUser(CollectionBaseClass): ...@@ -27,12 +27,14 @@ class MongoUser(CollectionBaseClass):
def __init__(self): def __init__(self):
super().__init__(mongo_client, Databases.DB_NAME, collection_name) super().__init__(mongo_client, Databases.DB_NAME, collection_name)
# fetching the user details based on the email id
def fetch_user_details(self, email): def fetch_user_details(self, email):
if user := self.find_one(query={self.key_email: email}): if user := self.find_one(query={self.key_email: email}):
return user return user
return None return None
def insert_user(self, query): # updating the login time
if user := self.insert_one(data=query): def update_user(self, update, query):
if user := self.update_one(query=update, data=query):
return user return user
return None return None
...@@ -2,8 +2,10 @@ import redis ...@@ -2,8 +2,10 @@ import redis
from scripts.config import Databases from scripts.config import Databases
# creating the redis connection
redis_uri = Databases.REDIS_URI redis_uri = Databases.REDIS_URI
# user login db
login_db = redis.from_url( login_db = redis.from_url(
redis_uri, db=int(Databases.REDIS_LOGIN_DB), decode_responses=True redis_uri, db=int(Databases.REDIS_LOGIN_DB), decode_responses=True
) )
...@@ -6,7 +6,9 @@ class ErrorMessages: ...@@ -6,7 +6,9 @@ class ErrorMessages:
ERROR_INVALID_LOGIN = "Your are not authorized to view this website." ERROR_INVALID_LOGIN = "Your are not authorized to view this website."
ERROR_INVALID_USERNAME = "Invalid Username" ERROR_INVALID_USERNAME = "Invalid Username"
ERROR_INVALID_PASSWORD = "Invalid Password" ERROR_INVALID_PASSWORD = "Invalid Password"
ERROR_INVALID_PROJECT_ID = "Invalid Project Id"
ERROR_UNAUTHORIZED_USER_LOGIN = "Account is not available" ERROR_UNAUTHORIZED_USER_LOGIN = "Account is not available"
ERROR_LOGIN_TYPE_INVALID = "Invalid Login Method"
ERROR_USER_NOT_REGISTERED = "Account is not registered in the portal." ERROR_USER_NOT_REGISTERED = "Account is not registered in the portal."
ERROR_PASSWORD_MISMATCH = "Passwords Authentication Failed. Please enter the correct password" ERROR_PASSWORD_MISMATCH = "Passwords Authentication Failed. Please enter the correct password"
ERROR_TOKEN_GENERATION = "Unsuccessful token generation" ERROR_TOKEN_GENERATION = "Unsuccessful token generation"
...@@ -732,3 +732,148 @@ TypeError: unsupported type for timedelta minutes component: str ...@@ -732,3 +732,148 @@ TypeError: unsupported type for timedelta minutes component: str
2023-03-22 19:02:59 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671 2023-03-22 19:02:59 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-22 19:06:01 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671 2023-03-22 19:06:01 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-22 19:14:25 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671 2023-03-22 19:14:25 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:41:43 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:43:55 - ERROR - [MainThread:db_data_validation(): 81] - 'dict' object has no attribute 'username'
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 77, in db_data_validation
return False, {"message": ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN, "data": login_data.username}
AttributeError: 'dict' object has no attribute 'username'
2023-03-23 09:43:55 - ERROR - [MainThread:db_password_matching(): 97] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 86, in db_password_matching
response, message = self.db_data_validation(project_id, login_data)
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:43:55 - ERROR - [MainThread:login_default(): 37] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 24, in login_default
return obj_login_handler.normal_login(user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 22, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.project_id,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:51:09 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:51:24 - ERROR - [MainThread:login_default(): 37] - db_password_matching() takes 3 positional arguments but 4 were given
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 24, in login_default
return obj_login_handler.normal_login(user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 25, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.project_id,
TypeError: db_password_matching() takes 3 positional arguments but 4 were given
2023-03-23 09:51:53 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:51:57 - ERROR - [MainThread:db_data_validation(): 82] - string indices must be integers
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 71, in db_data_validation
self.db_user_data = MongoUser().fetch_user_details(login_data["username"])
TypeError: string indices must be integers
2023-03-23 09:51:57 - ERROR - [MainThread:db_password_matching(): 98] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 87, in db_password_matching
response, message = self.db_data_validation(login_data)
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:51:57 - ERROR - [MainThread:login_default(): 37] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 24, in login_default
return obj_login_handler.normal_login(user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 25, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.project_id,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:52:29 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:52:34 - ERROR - [MainThread:db_data_validation(): 82] - string indices must be integers
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 71, in db_data_validation
self.db_user_data = MongoUser().fetch_user_details(login_data["username"])
TypeError: string indices must be integers
2023-03-23 09:52:34 - ERROR - [MainThread:db_password_matching(): 98] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 87, in db_password_matching
response, message = self.db_data_validation(login_data)
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:52:34 - ERROR - [MainThread:login_default(): 37] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 24, in login_default
return obj_login_handler.normal_login(user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 25, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.project_id,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:53:43 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 09:53:48 - ERROR - [MainThread:db_data_validation(): 82] - string indices must be integers
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 71, in db_data_validation
self.db_user_data = MongoUser().fetch_user_details(login_data["username"])
TypeError: string indices must be integers
2023-03-23 09:53:48 - ERROR - [MainThread:db_password_matching(): 98] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 87, in db_password_matching
response, message = self.db_data_validation(payload["username"])
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:53:48 - ERROR - [MainThread:login_default(): 37] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 24, in login_default
return obj_login_handler.normal_login(user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 25, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.payload,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 09:54:58 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:36:39 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:46:17 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:46:55 - ERROR - [MainThread:password_decrypt(): 51] - Invalid base64-encoded string: number of data characters (57) cannot be 1 more than a multiple of 4
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 36, in password_decrypt
enc = base64.b64decode(password)
File "C:\Users\arun.uday\AppData\Local\Programs\Python\Python39\lib\base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (57) cannot be 1 more than a multiple of 4
2023-03-23 10:46:55 - ERROR - [MainThread:db_password_matching(): 101] - secret must be unicode or bytes, not None
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 95, in db_password_matching
if not self.pwd_context.verify(password, self.db_user_data["password"]):
File "E:\Git\meta-services\venv\lib\site-packages\passlib\context.py", line 2347, in verify
return record.verify(secret, hash, **kwds)
File "E:\Git\meta-services\venv\lib\site-packages\passlib\utils\handlers.py", line 787, in verify
validate_secret(secret)
File "E:\Git\meta-services\venv\lib\site-packages\passlib\utils\handlers.py", line 122, in validate_secret
raise exc.ExpectedStringError(secret, "secret")
TypeError: secret must be unicode or bytes, not None
2023-03-23 10:46:55 - ERROR - [MainThread:login_default(): 35] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 28, in login_default
return login_mapper[user_data.login_type](user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 25, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.payload,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 10:49:33 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:49:55 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:50:09 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:50:39 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:51:04 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:51:20 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:51:29 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:52:19 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:52:20 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:52:25 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:55:06 - ERROR - [MainThread:password_decrypt(): 51] - 'utf-8' codec can't decode byte 0xab in position 1: invalid start byte
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 49, in password_decrypt
return data.decode(Services.ENCODING_TYPE)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xab in position 1: invalid start byte
2023-03-23 10:55:06 - ERROR - [MainThread:db_password_matching(): 103] - secret must be unicode or bytes, not None
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\core\handlers\normal_login.py", line 97, in db_password_matching
if not self.pwd_context.verify(password, self.db_user_data["password"]):
File "E:\Git\meta-services\venv\lib\site-packages\passlib\context.py", line 2347, in verify
return record.verify(secret, hash, **kwds)
File "E:\Git\meta-services\venv\lib\site-packages\passlib\utils\handlers.py", line 787, in verify
validate_secret(secret)
File "E:\Git\meta-services\venv\lib\site-packages\passlib\utils\handlers.py", line 122, in validate_secret
raise exc.ExpectedStringError(secret, "secret")
TypeError: secret must be unicode or bytes, not None
2023-03-23 10:55:06 - ERROR - [MainThread:login_default(): 35] - cannot unpack non-iterable NoneType object
Traceback (most recent call last):
File "E:\Git\meta-services\scripts\services\v1\iot_manager_services.py", line 28, in login_default
return login_mapper[user_data.login_type](user_data, request)
File "E:\Git\meta-services\scripts\core\handlers\login_handler.py", line 28, in normal_login
response, data = self.obj_login_handler.db_password_matching(login_data.payload,
TypeError: cannot unpack non-iterable NoneType object
2023-03-23 10:57:32 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 10:59:57 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 11:15:51 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
2023-03-23 11:17:30 - INFO - [MainThread:<module>(): 37] - App Starting at 0.0.0.0:8671
from typing import Union
from fastapi import APIRouter, HTTPException, status, Depends from fastapi import APIRouter, HTTPException, status, Depends
from scripts.constants.api import ApiEndPoints from scripts.constants.api import ApiEndPoints
...@@ -19,20 +17,22 @@ async def login_default( ...@@ -19,20 +17,22 @@ async def login_default(
user_data: LoginRequest, request: MetaInfoSchema = Depends(auth) user_data: LoginRequest, request: MetaInfoSchema = Depends(auth)
): ):
try: try:
# v1
if user_data.login_type == "normal":
return obj_login_handler.normal_login(user_data, request)
# v1
elif user_data.login_type == "google":
return obj_login_handler.google_login(user_data)
# v2 # mapper for login types
elif user_data.login_type == "microsoft": login_mapper = {
return obj_login_handler.microsoft_login(user_data) "normal": obj_login_handler.normal_login,
"google": obj_login_handler.google_login,
"microsoft": obj_login_handler.microsoft_login
}
# getting the functions based on the login types
if user_data.login_type in login_mapper:
return login_mapper[user_data.login_type](user_data, request)
else: else:
return HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid Request") return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid Request")
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
......
import uuid from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from scripts.config import Secrets from scripts.config import Secrets
from scripts.database.mongo.mongo_login import MongoUser
from scripts.database.redis.redis_conn import login_db from scripts.database.redis.redis_conn import login_db
from scripts.utils.security.jwt_util import JWT from scripts.utils.security.jwt_util import JWT
jwt = JWT() jwt = JWT()
mongo_user = MongoUser()
def create_token( def create_token(
...@@ -17,17 +18,25 @@ def create_token( ...@@ -17,17 +18,25 @@ def create_token(
""" """
This method is to create a cookie This method is to create a cookie
""" """
# creating the payload
payload = {"ip": ip, "user_id": user_id, "token": Secrets.SECRET_KEY, "age": age} payload = {"ip": ip, "user_id": user_id, "token": Secrets.SECRET_KEY, "age": age}
if project_id: if project_id:
payload["project_id"] = project_id payload["project_id"] = project_id
exp = datetime.now() + timedelta(minutes=age) # getting the current time
current_time = datetime.now()
# generating the expiry time of the token
exp = current_time + timedelta(minutes=age)
# creating the dictionary with issuer and expiry time
_extras = {"iss": Secrets.issuer, "exp": exp} _extras = {"iss": Secrets.issuer, "exp": exp}
_payload = payload | _extras _payload = payload | _extras
# encoding the token
new_token = jwt.encode(_payload) new_token = jwt.encode(_payload)
# Add session to redis # Add session to redis
login_db.set(user_id, new_token) login_db.set(user_id, new_token)
login_db.expire(user_id, timedelta(minutes=age)) login_db.expire(user_id, timedelta(minutes=age))
# Add updated time to mongo db
mongo_user.update_user({"email": user_id}, {"updated_at": current_time})
return user_id return user_id
...@@ -45,24 +45,30 @@ class _CookieAuthentication(APIKeyBase): ...@@ -45,24 +45,30 @@ class _CookieAuthentication(APIKeyBase):
self.jwt = JWT() self.jwt = JWT()
def __call__(self, request: Request, response: Response) -> MetaInfoSchema: def __call__(self, request: Request, response: Response) -> MetaInfoSchema:
# getting the cookie from the request
cookies = request.cookies cookies = request.cookies
# checking if the request has valid cookie
login_token = cookies.get(self.cookie_name) or request.headers.get( login_token = cookies.get(self.cookie_name) or request.headers.get(
self.cookie_name self.cookie_name
) )
if not login_token or login_token != Services.PROJECT_NAME: if not login_token or login_token != Services.PROJECT_NAME:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
# if the cookie name is same as the service name
if login_token == Services.PROJECT_NAME: if login_token == Services.PROJECT_NAME:
return MetaInfoSchema( return MetaInfoSchema(
project_id=Services.PROJECT_ID, project_id=Services.PROJECT_ID,
ip_address=request.client.host, # type: ignore ip_address=request.client.host, # type: ignore
login_token=cookies.get("login-token"), login_token=cookies.get("login-token"),
) )
# getting the token stored in redis based on the cookie value
jwt_token = self.login_redis.get(login_token) jwt_token = self.login_redis.get(login_token)
if not jwt_token: if not jwt_token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
try: try:
# validating the token
decoded_token = self.jwt.validate(token=jwt_token) decoded_token = self.jwt.validate(token=jwt_token)
if not decoded_token: if not decoded_token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
...@@ -73,6 +79,7 @@ class _CookieAuthentication(APIKeyBase): ...@@ -73,6 +79,7 @@ class _CookieAuthentication(APIKeyBase):
detail=ErrorMessages.UNKNOWN_ERROR, detail=ErrorMessages.UNKNOWN_ERROR,
) )
# checking if the token has necessary fields
user_id = decoded_token.get("user_id") user_id = decoded_token.get("user_id")
project_id = decoded_token.get("project_id") project_id = decoded_token.get("project_id")
if not user_id or not project_id: if not user_id or not project_id:
......
...@@ -8,10 +8,11 @@ from scripts.config import Secrets ...@@ -8,10 +8,11 @@ from scripts.config import Secrets
class JWT: class JWT:
def __init__(self) -> None: def __init__(self) -> None:
self.max_login_age: int = Secrets.ACCESS_TOKEN_EXPIRE_MINUTES self.max_login_age: int = Secrets.ACCESS_TOKEN_EXPIRE_MINUTES
# self.issuer: str = Secrets.issuer self.issuer: str = Secrets.issuer
self.alg: str = Secrets.ALGORITHM self.alg: str = Secrets.ALGORITHM
self.key = Secrets.SECRET_KEY self.key = Secrets.SECRET_KEY
# encoding the payload
def encode(self, payload) -> str: def encode(self, payload) -> str:
try: try:
return jwt.encode(payload, self.key, algorithm=self.alg) return jwt.encode(payload, self.key, algorithm=self.alg)
...@@ -19,6 +20,7 @@ class JWT: ...@@ -19,6 +20,7 @@ class JWT:
logging.exception(f"Exception while encoding JWT: {str(e)}") logging.exception(f"Exception while encoding JWT: {str(e)}")
raise raise
# decoding the payload
def decode(self, token): def decode(self, token):
try: try:
return jwt.decode(token, self.key, algorithms=self.alg) return jwt.decode(token, self.key, algorithms=self.alg)
...@@ -26,12 +28,15 @@ class JWT: ...@@ -26,12 +28,15 @@ class JWT:
logging.exception(f"Exception while encoding JWT: {str(e)}") logging.exception(f"Exception while encoding JWT: {str(e)}")
raise raise
# validate the payload
def validate(self, token): def validate(self, token):
try: try:
return jwt.decode( return jwt.decode(
token, token,
self.key, self.key,
algorithms=self.alg algorithms=self.alg,
leeway=Secrets.leeway_in_minutes,
options={"require": ["exp", "iss"]},
) )
except Exception as e: except Exception as e:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment