292 lines
9.6 KiB
Python
292 lines
9.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status, Request, Query, Body
|
|||
|
|
from sqlalchemy.orm import Session
|
||
|
|
from typing import List, Optional
|
||
|
|
|
||
|
|
from app.core.deps import get_db, require_current_user, get_current_user_token
|
||
|
|
from app.core.security import verify_password, get_password_hash, create_access_token
|
||
|
|
from app.auth.models import AppUser, Role
|
||
|
|
from app.auth.schemas import (
|
||
|
|
LoginRequest,
|
||
|
|
SessionResponse,
|
||
|
|
SessionItem,
|
||
|
|
RegistrationRequest,
|
||
|
|
SendEmailRequest,
|
||
|
|
EmailExistsRequest,
|
||
|
|
UserResponse
|
||
|
|
)
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/token", tags=["Authentication & User Session"])
|
||
|
|
|
||
|
|
@router.post("/session", response_model=SessionResponse)
|
||
|
|
def login_session(login_req: LoginRequest, db: Session = Depends(get_db)):
|
||
|
|
"""
|
||
|
|
Authenticates user and returns the exact SessionResponse expected by Angular Clarity login.service.ts.
|
||
|
|
"""
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == login_req.email).first()
|
||
|
|
if not user or not verify_password(login_req.password, user.password):
|
||
|
|
return SessionResponse(
|
||
|
|
operationStatus="ERROR",
|
||
|
|
operationMessage="Login Failed",
|
||
|
|
item=None
|
||
|
|
)
|
||
|
|
|
||
|
|
# Collect role descriptions / names
|
||
|
|
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
|
||
|
|
|
||
|
|
# Generate JWT
|
||
|
|
token = create_access_token(
|
||
|
|
subject=user.user_id,
|
||
|
|
extra_claims={
|
||
|
|
"email": user.email,
|
||
|
|
"username": user.username,
|
||
|
|
"roles": roles,
|
||
|
|
"user_grp": user.user_grp_id
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
session_item = SessionItem(
|
||
|
|
userId=user.user_id,
|
||
|
|
email=user.email,
|
||
|
|
fullname=user.full_name or user.username,
|
||
|
|
firstName=user.first_name or user.full_name or user.username,
|
||
|
|
username=user.username,
|
||
|
|
roles=roles,
|
||
|
|
token=token
|
||
|
|
)
|
||
|
|
|
||
|
|
return SessionResponse(
|
||
|
|
operationStatus="SUCCESS",
|
||
|
|
operationMessage="Login Success",
|
||
|
|
item=session_item
|
||
|
|
)
|
||
|
|
|
||
|
|
@router.post("/generate-token", response_model=SessionResponse)
|
||
|
|
def generate_token(login_req: LoginRequest, db: Session = Depends(get_db)):
|
||
|
|
"""Alias for /token/session."""
|
||
|
|
return login_session(login_req, db)
|
||
|
|
|
||
|
|
@router.get("/logout")
|
||
|
|
@router.post("/logout")
|
||
|
|
def logout_user():
|
||
|
|
"""Logs out the user."""
|
||
|
|
return {"operationStatus": "SUCCESS", "operationMessage": "Logged out successfully"}
|
||
|
|
|
||
|
|
@router.get("/user")
|
||
|
|
def get_current_logged_in_user(
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
token_payload: dict = Depends(require_current_user)
|
||
|
|
):
|
||
|
|
"""Returns current user details based on the JWT token."""
|
||
|
|
user_id = token_payload.get("user_id") or token_payload.get("sub")
|
||
|
|
user = db.query(AppUser).filter(AppUser.user_id == user_id).first()
|
||
|
|
if not user:
|
||
|
|
raise HTTPException(status_code=404, detail="User not found")
|
||
|
|
|
||
|
|
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
|
||
|
|
return {
|
||
|
|
"userId": user.user_id,
|
||
|
|
"email": user.email,
|
||
|
|
"username": user.username,
|
||
|
|
"fullname": user.full_name,
|
||
|
|
"roles": roles,
|
||
|
|
"status": user.status
|
||
|
|
}
|
||
|
|
|
||
|
|
@router.get("/getuser/{user_id}")
|
||
|
|
def get_user_by_id(user_id: int, db: Session = Depends(get_db)):
|
||
|
|
"""Returns user by ID."""
|
||
|
|
user = db.query(AppUser).filter(AppUser.user_id == user_id).first()
|
||
|
|
if not user:
|
||
|
|
raise HTTPException(status_code=404, detail="User not found")
|
||
|
|
|
||
|
|
roles = [r.description or r.name for r in user.roles] if user.roles else ["Admin"]
|
||
|
|
return {
|
||
|
|
"userId": user.user_id,
|
||
|
|
"email": user.email,
|
||
|
|
"username": user.username,
|
||
|
|
"fullname": user.full_name,
|
||
|
|
"roles": roles
|
||
|
|
}
|
||
|
|
|
||
|
|
@router.post("/email-exists")
|
||
|
|
def check_email_exists(req: EmailExistsRequest, db: Session = Depends(get_db)):
|
||
|
|
"""Checks if email already exists."""
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == req.email).first()
|
||
|
|
return {"exists": user is not None}
|
||
|
|
|
||
|
|
@router.post("/users/sysaccount/savesysaccount")
|
||
|
|
@router.post("/savesysaccount")
|
||
|
|
def save_sys_account(data: dict = Body(...)):
|
||
|
|
"""
|
||
|
|
Saves sysaccount during Angular user signup (mirrors Spring Boot SysAccountController /savesysaccount).
|
||
|
|
"""
|
||
|
|
account_id = data.get("account_id") or 1
|
||
|
|
res = dict(data)
|
||
|
|
res["account_id"] = account_id
|
||
|
|
res["mongoid"] = f"acc_{account_id}"
|
||
|
|
return res
|
||
|
|
|
||
|
|
@router.post("/addOneAppUser")
|
||
|
|
def register_user(reg: RegistrationRequest, db: Session = Depends(get_db)):
|
||
|
|
"""
|
||
|
|
Registers a new user or activates a pending user created during OTP step.
|
||
|
|
Mirrors Spring Boot SessionController /addOneAppUser.
|
||
|
|
"""
|
||
|
|
email = reg.email.strip()
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == email).first()
|
||
|
|
|
||
|
|
raw_pwd = reg.new_password or reg.password
|
||
|
|
if not raw_pwd:
|
||
|
|
raise HTTPException(status_code=400, detail="Password is required")
|
||
|
|
|
||
|
|
hashed_pwd = get_password_hash(raw_pwd)
|
||
|
|
first_name = reg.first_name or reg.fullName or email.split("@")[0]
|
||
|
|
last_name = reg.last_name or ""
|
||
|
|
full_name = reg.fullName or f"{first_name} {last_name}".strip()
|
||
|
|
username = reg.username or email.split("@")[0]
|
||
|
|
|
||
|
|
if user:
|
||
|
|
if user.status == "ACTIVE" and user.password:
|
||
|
|
raise HTTPException(status_code=400, detail="Email already exists")
|
||
|
|
# Activate pending user
|
||
|
|
user.password = hashed_pwd
|
||
|
|
user.first_name = first_name
|
||
|
|
user.last_name = last_name
|
||
|
|
user.full_name = full_name
|
||
|
|
user.username = username
|
||
|
|
user.status = "ACTIVE"
|
||
|
|
user.is_active = True
|
||
|
|
else:
|
||
|
|
user = AppUser(
|
||
|
|
email=email,
|
||
|
|
username=username,
|
||
|
|
password=hashed_pwd,
|
||
|
|
full_name=full_name,
|
||
|
|
first_name=first_name,
|
||
|
|
last_name=last_name,
|
||
|
|
status="ACTIVE",
|
||
|
|
user_grp_id=1,
|
||
|
|
is_active=True
|
||
|
|
)
|
||
|
|
db.add(user)
|
||
|
|
|
||
|
|
# Assign Admin role
|
||
|
|
admin_role = db.query(Role).filter(Role.name == "ROLE_ADMIN").first()
|
||
|
|
if admin_role and admin_role not in user.roles:
|
||
|
|
user.roles.append(admin_role)
|
||
|
|
|
||
|
|
db.commit()
|
||
|
|
db.refresh(user)
|
||
|
|
|
||
|
|
return {
|
||
|
|
"userId": user.user_id,
|
||
|
|
"email": user.email,
|
||
|
|
"username": user.username,
|
||
|
|
"fullName": user.full_name,
|
||
|
|
"firstName": user.first_name,
|
||
|
|
"roles": [r.description or r.name for r in user.roles],
|
||
|
|
"message": "User registered successfully"
|
||
|
|
}
|
||
|
|
|
||
|
|
@router.post("/user/send_email")
|
||
|
|
def send_email_for_otp(req: SendEmailRequest, db: Session = Depends(get_db)):
|
||
|
|
"""
|
||
|
|
Sends OTP for email verification (mirrors Spring Boot SessionController /token/user/send_email).
|
||
|
|
"""
|
||
|
|
import random
|
||
|
|
from app.core.email import email_service
|
||
|
|
|
||
|
|
email = req.email.strip()
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == email).first()
|
||
|
|
if user and user.status == "ACTIVE" and user.password: # Already registered active user
|
||
|
|
raise HTTPException(status_code=400, detail=f"{email} already exists")
|
||
|
|
|
||
|
|
otp = str(random.randint(100000, 999999))
|
||
|
|
if not user:
|
||
|
|
username = req.username or email.split("@")[0]
|
||
|
|
user = AppUser(
|
||
|
|
email=email,
|
||
|
|
username=username,
|
||
|
|
password="", # Empty string satisfies NOT NULL constraint until password is set
|
||
|
|
full_name=req.fullName or username,
|
||
|
|
first_name=req.fullName or username,
|
||
|
|
status="PENDING",
|
||
|
|
random_no=otp,
|
||
|
|
is_active=False
|
||
|
|
)
|
||
|
|
db.add(user)
|
||
|
|
else:
|
||
|
|
user.random_no = otp
|
||
|
|
db.commit()
|
||
|
|
|
||
|
|
email_service.adduserviaadmin(email, "Email Verification", otp)
|
||
|
|
return {
|
||
|
|
"operationStatus": "SUCCESS",
|
||
|
|
"operationMessage": "Otp send successfully",
|
||
|
|
"msg": "Otp send successfully",
|
||
|
|
"otp": otp
|
||
|
|
}
|
||
|
|
|
||
|
|
@router.post("/user/resend_otp")
|
||
|
|
def resend_otp(
|
||
|
|
email: Optional[str] = Query(None),
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
body: Optional[SendEmailRequest] = None
|
||
|
|
):
|
||
|
|
"""
|
||
|
|
Resends OTP for email verification (mirrors Spring Boot SessionController /token/user/resend_otp).
|
||
|
|
Supports query parameter (?email=...) or JSON body.
|
||
|
|
"""
|
||
|
|
import random
|
||
|
|
from app.core.email import email_service
|
||
|
|
|
||
|
|
target_email = email or (body.email if body else None)
|
||
|
|
if not target_email:
|
||
|
|
raise HTTPException(status_code=400, detail="Email is required")
|
||
|
|
|
||
|
|
target_email = target_email.strip()
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == target_email).first()
|
||
|
|
if not user:
|
||
|
|
raise HTTPException(status_code=400, detail=f"{target_email} does not exist")
|
||
|
|
|
||
|
|
otp = str(random.randint(100000, 999999))
|
||
|
|
user.random_no = otp
|
||
|
|
db.commit()
|
||
|
|
|
||
|
|
email_service.adduserviaadmin(target_email, "Email Verification - Resend OTP", otp)
|
||
|
|
return {
|
||
|
|
"operationStatus": "SUCCESS",
|
||
|
|
"operationMessage": "resend Otp send successfully",
|
||
|
|
"msg": "resend Otp send successfully",
|
||
|
|
"otp": otp
|
||
|
|
}
|
||
|
|
|
||
|
|
@router.post("/user/otp_verification")
|
||
|
|
def otp_verification(
|
||
|
|
email: str = Query(...),
|
||
|
|
otp: str = Query(...),
|
||
|
|
db: Session = Depends(get_db)
|
||
|
|
):
|
||
|
|
"""
|
||
|
|
Verifies OTP entered by user (mirrors Spring Boot SessionController /token/user/otp_verification).
|
||
|
|
Query parameters: ?email=...&otp=...
|
||
|
|
"""
|
||
|
|
email_clean = email.replace(" ", "+").strip()
|
||
|
|
user = db.query(AppUser).filter(AppUser.email == email_clean).first()
|
||
|
|
if not user:
|
||
|
|
raise HTTPException(status_code=400, detail="User not found")
|
||
|
|
|
||
|
|
if user.random_no and user.random_no.strip() == otp.strip():
|
||
|
|
return {
|
||
|
|
"operationStatus": "SUCCESS",
|
||
|
|
"operationMessage": "OTP Verified",
|
||
|
|
"msg": "OTP Verified"
|
||
|
|
}
|
||
|
|
else:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||
|
|
detail="Wrong OTP"
|
||
|
|
)
|
||
|
|
|
||
|
|
|