Commit 8f0c4a48 authored by arun.uday's avatar arun.uday

AssetManager-V1.0- Not reviewed

Updated code to include user id validations, cookie authentication, changed the api's for add, update, delete to a single api, given separate file for password encryption, new api's created based on UI requirements, add details according to the Ui needs.
parent 27bc1824
......@@ -22,6 +22,7 @@ class _Services(BaseSettings):
EMAIL_PASSWORD: str
HTML_LINK: str
RESET_ENDPOINT: str
DATE_TIME = '%Y-%m-%d %H:%M:%S'
class _Databases(BaseSettings):
......@@ -41,7 +42,7 @@ class _PathConf:
class _Secrets(BaseSettings):
ACCESS_TOKEN_EXPIRE_MINUTES = 30
ACCESS_TOKEN_EXPIRE_MINUTES = 480
TOKEN_EXPIRE_TIME = 5
leeway_in_minutes: int = 10
KEY_ENCRYPTION: str
......
......@@ -4,15 +4,17 @@ class ApiEndPoints:
# common
submit: str = "/submit"
add: str = "/add"
view: str = "/view"
add: str = "/adduser"
view: str = "/table_body"
table_actions: str = "/table_actions"
update: str = "/update"
delete: str = "/delete"
header: str = "/header"
header: str = "/table_header"
download: str = "/download"
search: str = "/search"
forgot: str = "/forgot"
reset: str = "/reset"
logout: str = "/logout"
# login-management
asset_manager_login: str = "/login"
......@@ -24,9 +26,12 @@ class ApiEndPoints:
asset_manager_user_management: str = "/users"
asset_manager_user_add: str = asset_manager_user_management + add
asset_manager_user_view: str = asset_manager_user_management + view
asset_manager_user_header: str = asset_manager_user_management + header
asset_manager_user_table_actions: str = asset_manager_user_management + table_actions
asset_manager_user_update: str = asset_manager_user_management + update
asset_manager_user_delete: str = asset_manager_user_management + delete
asset_manager_user_search: str = asset_manager_user_management + search
asset_manager_user_logout: str = asset_manager_user_management + logout
# dashboard-management
asset_manager_dashboard: str = "/dashboard"
......
class Validations:
email = "user@example.com"
......@@ -8,12 +8,14 @@ from scripts.utils.response_utils import ResponseData
obj_download_util = ResponseData()
# download header and row data
class DashboardManagement:
def __init__(self):
self.download_files = obj_download_util.download_file_data()
def download_header(self):
try:
# header contents
data = {
"actions": [
{
......@@ -26,10 +28,13 @@ class DashboardManagement:
{
"headerName": "File Name",
"field": "file_name",
"key": "file_name"
"key": "file_name",
"flex": 0,
"width": 1010
}],
}
# column urls
column_urls = [{"file_name": key, "file_url": value} for key, value in self.download_files.items()]
data["rowData"] = column_urls
return JSONResponse(
......
......@@ -12,7 +12,7 @@ from scripts.core.handlers.normal_login import NormalLogin
from scripts.database.mongo.mongo_db import MongoUser
from scripts.errors import ErrorMessages
from scripts.logging.logger import logger
from scripts.schemas.default_responses import DefaultFailureResponse, DefaultResponse
from scripts.schemas.default_responses import DefaultFailureResponse, DefaultResponse, DefaultSuccessResponse
from scripts.utils.security.jwt_util import JWT
from scripts.utils.security.password_util import EncryptDecryptPassword
......@@ -48,9 +48,8 @@ class LoginHandlers:
return JSONResponse(content=DefaultFailureResponse(status="failed",
message=data).dict(),
status_code=status.HTTP_200_OK)
# generating the access tokens
responses, exp = self.obj_login_handler.generate_cookie_tokens(user_data, request)
responses, exp = self.obj_login_handler.generate_cookie_tokens(data, request)
# token generation unsuccessful
if responses is None:
return JSONResponse(
......@@ -59,12 +58,13 @@ class LoginHandlers:
status_code=status.HTTP_200_OK)
# sending successful response to UI
response = JSONResponse(
content=DefaultResponse(status="success", message="Logged In Successfully", data=data).dict(),
content=DefaultResponse(status="success", message="Logged In Successfully",
data=data).dict(),
status_code=status.HTTP_200_OK, headers={"Content-Type": "application/json"})
response.set_cookie(key="login-token", value=responses, expires=exp)
return response
# v1
# v2
def google_login(self, request):
pass
......@@ -72,6 +72,7 @@ class LoginHandlers:
def microsoft_login(self, request):
pass
# forgot password handler
@staticmethod
def forgot_password_handler(email):
try:
......@@ -79,12 +80,12 @@ class LoginHandlers:
# If user exists, send forgot password email with JWT token
# This should include email and expire time
# Send email using MIME
db_user_data = obj_mongo_user.fetch_one_user_details(email)
db_user_data = obj_mongo_user.fetch_one_user_details({"email": email})
# if the user is not available
if not db_user_data:
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_EMAIL_ID_DOESNT_EXIST).dict(),
message=ErrorMessages.ERROR_USER_ID_DOESNT_EXIST).dict(),
status_code=status.HTTP_200_OK)
mail = MIMEMultipart()
mail['From'] = Services.EMAIL_SENDER
......@@ -112,8 +113,7 @@ class LoginHandlers:
smtp.sendmail(Services.EMAIL_SENDER, email, mail.as_string())
return JSONResponse(
content=DefaultResponse(status="success", message="Email Send Successfully",
data={"username": email}).dict(),
content=DefaultSuccessResponse(status="success", message="Email Send Successfully").dict(),
status_code=status.HTTP_200_OK)
except Exception as e:
logger.exception(e)
......@@ -5,7 +5,6 @@ from datetime import datetime
from passlib.context import CryptContext
from validate_email import validate_email
from scripts.config import Secrets
from scripts.database.mongo.mongo_db import MongoUser
from scripts.errors import ErrorMessages
from scripts.logging.logger import logger
......@@ -20,6 +19,7 @@ class NormalLogin:
self.dt = datetime.now()
self.time_dt = datetime.now()
# user data validation
@staticmethod
def user_data_validation(email, password) -> dict | None:
try:
......@@ -33,10 +33,11 @@ class NormalLogin:
except Exception as e:
logger.exception(e)
# db validation
def db_data_validation(self, login_type, email):
try:
# fetching the data based on the username
self.db_user_data = MongoUser().fetch_one_user_details(email)
self.db_user_data = MongoUser().fetch_one_user_details({"email": email})
# if the user is not available
if not self.db_user_data:
return False, ErrorMessages.ERROR_UNAUTHORIZED_USER_LOGIN
......@@ -48,6 +49,7 @@ class NormalLogin:
except Exception as e:
logger.exception(e)
# matching the password
def db_password_matching(self, login_type, user_data, password):
try:
# getting the response after checking for the user data in db
......@@ -59,17 +61,19 @@ class NormalLogin:
if not self.pwd_context.verify(password, self.db_user_data["password"]):
return False, ErrorMessages.ERROR_PASSWORD_MISMATCH
# if the password is correct
return None, {"username": user_data.email, "role": self.db_user_data["user_role"]}
return None, {"user_id": self.db_user_data["user_id"], "name": self.db_user_data["name"],
"email": user_data.email,
"user_role": self.db_user_data["user_role"]}
except Exception as e:
logger.exception(e)
# cookie and token creation
@staticmethod
def generate_cookie_tokens(user_data, request):
try:
# creating the access token
access_token, exp = create_token(
user_id=user_data.email,
login_token=Secrets.SECRET_KEY,
user_id=user_data["user_id"],
ip=request.client.host
)
# returning the login token
......
......@@ -6,6 +6,7 @@ collection_name = DatabaseConstants.collection_user_details
class UserDetailsKeys:
KEY_USER_ID = "user_id"
KEY_NAME = "name"
KEY_EMAIL = "email"
KEY_PASSWORD = "password"
......@@ -17,6 +18,7 @@ class UserDetailsKeys:
class MongoUser(CollectionBaseClass):
key_name = UserDetailsKeys.KEY_NAME
key_user_id = UserDetailsKeys.KEY_USER_ID
key_email = UserDetailsKeys.KEY_EMAIL
key_password = UserDetailsKeys.KEY_PASSWORD
key_user_role = UserDetailsKeys.KEY_USER_ROLE
......@@ -28,16 +30,18 @@ class MongoUser(CollectionBaseClass):
super().__init__(mongo_client, Databases.DB_NAME, collection_name)
# fetching the user details based on the email id
def fetch_one_user_details(self, email):
if user := self.find_one(query={self.key_email: email}):
def fetch_one_user_details(self, query):
if user := self.find_one(query=query):
return user
return None
# fetching the user data
def fetch_all_user_details(self, query, filter_data):
if user := self.find(query=query, filter_dict=filter_data):
return user
return None
# inserting the user
def insert_new_user(self, data):
if user := self.insert_one(data=data):
return user
......@@ -54,3 +58,9 @@ class MongoUser(CollectionBaseClass):
if user := self.delete_one(query=query):
return user
return None
# for filtering
def filter_data_aggregate(self, pipeline):
if user := self.aggregate(pipelines=pipeline):
return user
return None
......@@ -14,6 +14,8 @@ class ErrorMessages:
ERROR_EMAIL_EXIST = "Email Id exists"
ERROR_IN_FETCHING = "Details cannot be fetched"
ERROR_IN_UPDATING = "Error in Updating"
ERROR_INVALID_REQUEST = "Invalid Request"
ERROR_USER_SESSION = "Not The Users Session"
# Data Validation
ERROR_INVALID_PASSWORD = "Invalid Password"
......@@ -21,4 +23,5 @@ class ErrorMessages:
ERROR_INVALID_EMAIL = "Invalid Email Id"
ERROR_INVALID_PHONE_NUMBER = "Invalid Phone Number"
ERROR_INVALID_USER_ROLE = "Invalid User Role"
ERROR_EMAIL_ID_DOESNT_EXIST = "Email Id doesn't exist"
ERROR_USER_ID_DOESNT_EXIST = "User Id doesn't exist"
ERROR_USER_ID = "User Id Not Required"
This diff is collapsed.
......@@ -10,6 +10,11 @@ class DefaultResponse(BaseModel):
data: Optional[Any]
class DefaultSuccessResponse(BaseModel):
status: str
message: str
# default failure responses
class DefaultFailureResponse(BaseModel):
status: str
......
......@@ -5,6 +5,7 @@ from pydantic import BaseModel
# model for login request
class LoginRequest(BaseModel):
login_type: str
email: str
password: str
......@@ -18,12 +19,26 @@ class RegistrationData(BaseModel):
user_role: str
class UserUpdate(BaseModel):
class UserActions(BaseModel):
action: str
user_id: Optional[str] = None
name: Optional[str] = None
email: Optional[str] = None
password: Optional[str]
phone_number: Optional[str] = None
login_type: Optional[str] = None
user_role: Optional[str] = None
class UsersFilter(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
user_role: Optional[str] = None
class UserIDValidation(BaseModel):
user_id: str
class EmailValidation(BaseModel):
email: str
......@@ -8,7 +8,7 @@ from scripts.core.handlers.user_management_handler import UserManagement
from scripts.errors import ErrorMessages
from scripts.logging.logger import logger
from scripts.schemas.default_responses import DefaultFailureResponse
from scripts.schemas.project_schema import LoginRequest, RegistrationData, UserUpdate, EmailValidation
from scripts.schemas.project_schema import LoginRequest, UserActions, EmailValidation, UserIDValidation
from scripts.utils.security.authorize_access import AuthorizeAccess
from scripts.utils.security.decorators import MetaInfoSchema, auth
......@@ -23,7 +23,6 @@ obj_dashboard_handler = DashboardManagement()
# login API
@router.post(ApiEndPoints.asset_manager_submit)
async def login_default(
login_type: str,
user_data: LoginRequest,
request: Request,
):
......@@ -36,12 +35,12 @@ async def login_default(
}
# getting the functions based on the login types
if login_type in login_mapper:
return login_mapper[login_type](user_data, request)
if user_data.login_type in login_mapper:
return login_mapper[user_data.login_type](user_data, request)
else:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid Request")
detail=ErrorMessages.ERROR_INVALID_REQUEST)
except Exception as e:
logger.exception(e)
......@@ -84,10 +83,10 @@ async def forgot_password(
status_code=status.HTTP_200_OK)
# Create new users API
@router.post(ApiEndPoints.asset_manager_user_add)
# User Management API
@router.post(ApiEndPoints.asset_manager_user_table_actions)
async def user_register(
user_data: RegistrationData,
user_data: UserActions,
request: MetaInfoSchema = Depends(auth)
):
try:
......@@ -97,21 +96,26 @@ async def user_register(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_UNAUTHORIZED_ACCESS).dict(),
status_code=status.HTTP_200_OK)
# mapper for login types
# mapper for registration types
register_mapper = {
"general": obj_user_handler.normal_register,
"general_login": obj_user_handler.normal_register,
"google": obj_user_handler.google_register,
"microsoft": obj_user_handler.microsoft_register
}
# getting the functions based on the login types
if user_data.login_type in register_mapper:
if user_data.action == "addnew" and user_data.login_type in register_mapper:
# registration
return register_mapper[user_data.login_type](user_data)
elif user_data.action == "edit":
# updating
return obj_user_handler.update_user_details(user_data)
elif user_data.action == "delete":
# deleting
return obj_user_handler.delete_user_details(user_data.user_id)
else:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid Request")
detail=ErrorMessages.ERROR_INVALID_REQUEST)
except Exception as e:
logger.exception(e)
return JSONResponse(
......@@ -120,44 +124,25 @@ async def user_register(
status_code=status.HTTP_200_OK)
# Update users API
@router.post(ApiEndPoints.asset_manager_user_update)
# View users API Header
@router.post(ApiEndPoints.asset_manager_user_header)
async def user_register(
email: str,
update_data: UserUpdate,
request: MetaInfoSchema = Depends(auth)
):
try:
# authorize the user
response = AuthorizeAccess().admin_authorize(request)
if not response:
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_UNAUTHORIZED_ACCESS).dict(),
status_code=status.HTTP_200_OK)
response = obj_user_handler.update_user_details(email, update_data)
return response
except Exception as e:
logger.exception(e)
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.OP_FAILED).dict(),
status_code=status.HTTP_200_OK)
# Delete users API
@router.post(ApiEndPoints.asset_manager_user_delete)
async def user_register(
email: str,
request: MetaInfoSchema = Depends(auth)
):
try:
response = AuthorizeAccess().admin_authorize(request)
# getting the header for the user table
response = obj_user_handler.fetch_view_header()
if not response:
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_UNAUTHORIZED_ACCESS).dict(),
status_code=status.HTTP_200_OK)
response = obj_user_handler.delete_user_details(email)
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ErrorMessages.ERROR_IN_FETCHING)
return response
except Exception as e:
logger.exception(e)
......@@ -173,6 +158,7 @@ async def user_register(
request: MetaInfoSchema = Depends(auth)
):
try:
# authorize the user
response = AuthorizeAccess().admin_authorize(request)
if not response:
return JSONResponse(
......@@ -196,15 +182,10 @@ async def user_register(
# download Button Dashboard
@router.post(ApiEndPoints.asset_manager_dashboard_download)
async def dashboard_download(
request: MetaInfoSchema = Depends(auth)
):
try:
response = AuthorizeAccess().admin_authorize(request)
if not response:
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_UNAUTHORIZED_ACCESS).dict(),
status_code=status.HTTP_200_OK)
# getting the data for the download dashboard
response = obj_dashboard_handler.download_header()
if not response:
return HTTPException(
......@@ -219,21 +200,16 @@ async def dashboard_download(
status_code=status.HTTP_200_OK)
# API for users search filter
@router.post(ApiEndPoints.asset_manager_user_search)
async def user_search_filter(
payload_data: Request,
# API for logout
@router.post(ApiEndPoints.asset_manager_user_logout)
async def user_logout(
user_id: UserIDValidation,
request: MetaInfoSchema = Depends(auth)
):
try:
response = AuthorizeAccess().admin_authorize(request)
if not response:
return JSONResponse(
content=DefaultFailureResponse(status="failed",
message=ErrorMessages.ERROR_UNAUTHORIZED_ACCESS).dict(),
status_code=status.HTTP_200_OK)
request_body = await payload_data.json()
response = obj_user_handler.user_filter_data(request_body)
# authorizing the user
# logging out
response = obj_user_handler.logout_user(user_id, request)
if not response:
return HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
......
......@@ -132,7 +132,7 @@
class="download-link"
style="color: blue"
target="_blank">Click here
to download</a>
to change password</a>
</td>
</tr>
</tbody>
......
......@@ -57,3 +57,6 @@ class MongoStageCreator:
def sort_stage(self, stage: dict) -> dict:
return self.add_stage("$sort", stage)
def regex_stage(self, stage: dict) -> dict:
return self.add_stage("$regex", stage)
......@@ -3,6 +3,7 @@ import re
from scripts.logging.logger import logger
# for data validations
class RegexValidation:
@staticmethod
def name_validation(name):
......
# response data utils
class ResponseData:
@staticmethod
def user_view():
def user_view_header():
header = {
"actions": [
{
......@@ -12,7 +13,7 @@ class ResponseData:
"class": "fa-trash",
"action": "delete",
"tooltip": "Delete"
}
},
], "externalActions": [
{
"type": "button",
......@@ -22,8 +23,8 @@ class ResponseData:
], "columnDefs": [
{
"headerName": "User Name",
"field": "userName",
"key": "userName"
"field": "email",
"key": "email"
},
{
"headerName": "Name",
......@@ -32,14 +33,17 @@ class ResponseData:
},
{
"headerName": "Role",
"field": "role",
"key": "role"
"field": "user_role",
"key": "user_role",
"width": 150,
"flex": 0
}
],
}
return header
# download api util
@staticmethod
def download_file_data():
data = {
......
......@@ -33,7 +33,6 @@ def create_token(
_payload = payload | _extras
# encoding the token
new_token = jwt.encode(_payload)
# Add session to redis
login_db.set(uid, new_token)
login_db.expire(uid, timedelta(minutes=age))
......
......@@ -5,10 +5,12 @@ obj_mongo_user = MongoUser()
class AuthorizeAccess:
@staticmethod
# authorize the user
def admin_authorize(request):
try:
user_data = obj_mongo_user.fetch_one_user_details(request.user_id)
if user_data["user_role"] != "super admin":
# returning the user details
user_data = obj_mongo_user.fetch_one_user_details({"user_id": request.user_id})
if user_data["user_role"] != "super_admin":
return False
return True
except TypeError:
......
......@@ -6,6 +6,7 @@ from scripts.config import Secrets, Services
from scripts.logging.logger import logger
# utility for the password
class EncryptDecryptPassword:
def __init__(self):
self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
......@@ -37,7 +38,10 @@ class EncryptDecryptPassword:
except Exception as e:
logger.exception(e)
# encrypting the password
def password_encrypt(self, password):
# decrypting the UI password
decrypted_password = self.password_decrypt(password)
# hashing the decrypted password
hashed_password = self.pwd_context.hash(decrypted_password.split("\"")[1])
return hashed_password
......@@ -6,6 +6,7 @@ from scripts.errors import ErrorMessages
from scripts.logging.logger import logger
# user data validations
class UserDataValidations:
@staticmethod
def register_data_validation(user_data, method, feature):
......
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