The PM panel (PmPanelPage -> iframe /pm/) needs the project-management platform served at /pm/ on the ERP domain. Source: org repo Aramesh/PM, branch feature/project-management-platform. Update via: git subtree pull --prefix=pm aramesh-pm feature/project-management-platform --squash
99 lines
3.7 KiB
Python
99 lines
3.7 KiB
Python
"""Local auth + role/scope introspection.
|
|
|
|
`POST /auth/login` is the real entry point; `GET /auth/me` re-reads the caller's
|
|
role/permissions/scope (used after a role or assignment change, and by the
|
|
frontend to self-heal a session stored before RBAC existed). The `/auth/dev-*`
|
|
endpoints implement the dashboard role switcher and only exist while
|
|
`PM_DEV_ROLE_SWITCH` is enabled.
|
|
"""
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.authz import build_context, dev_login_usernames, dev_switch_enabled
|
|
from app.db import get_db
|
|
from app.dev_accounts import demo_account, ensure_demo_assignments
|
|
from app.errors import AuthError, NotFoundError
|
|
from app.models import PMUser, ProjectAssignment
|
|
from app.security import authenticate, create_token, get_current_user
|
|
from app.serializers import user_payload
|
|
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
|
|
|
|
class LoginIn(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
@router.post("/login")
|
|
async def login(payload: LoginIn, db: AsyncSession = Depends(get_db)):
|
|
user = await authenticate(db, payload.username, payload.password)
|
|
if user is None:
|
|
raise AuthError("نام کاربری یا رمز عبور اشتباه است")
|
|
ctx = await build_context(db, user)
|
|
return {"data": {"token": create_token(user.id), "user": user_payload(ctx)}}
|
|
|
|
|
|
@router.get("/me")
|
|
async def me(user: PMUser = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
|
return {"data": user_payload(await build_context(db, user))}
|
|
|
|
|
|
# --- development role switcher -------------------------------------------------
|
|
|
|
|
|
def _require_dev_switch() -> None:
|
|
if not dev_switch_enabled():
|
|
raise NotFoundError("تغییر نقش فقط در فاز توسعه فعال است")
|
|
|
|
|
|
@router.get("/dev-roles")
|
|
async def dev_roles(db: AsyncSession = Depends(get_db)):
|
|
"""Demo identities offered by the dashboard role picker (dev only)."""
|
|
_require_dev_switch()
|
|
# projects only exist after the first pull — self-heal the demo scope here
|
|
await ensure_demo_assignments(db)
|
|
data = []
|
|
for username in dev_login_usernames():
|
|
account = demo_account(username)
|
|
if account is None:
|
|
continue
|
|
user = await db.scalar(select(PMUser).where(PMUser.username == account.username))
|
|
if user is None:
|
|
continue
|
|
projects = await db.scalar(
|
|
select(func.count()).where(ProjectAssignment.user_id == user.id)
|
|
)
|
|
data.append(
|
|
{
|
|
"username": user.username,
|
|
"display_name": user.display_name,
|
|
"role": account.role.value,
|
|
"project_count": projects or 0,
|
|
"all_projects": account.share is None,
|
|
}
|
|
)
|
|
return {"data": data}
|
|
|
|
|
|
class DevLoginIn(BaseModel):
|
|
username: str
|
|
|
|
|
|
@router.post("/dev-login")
|
|
async def dev_login(payload: DevLoginIn, db: AsyncSession = Depends(get_db)):
|
|
"""Issue a token for a demo account — dev role switching without a password.
|
|
|
|
Restricted to the configured demo usernames, never arbitrary users.
|
|
"""
|
|
_require_dev_switch()
|
|
if payload.username not in dev_login_usernames():
|
|
raise AuthError("این حساب در فهرست حسابهای توسعه نیست")
|
|
user = await db.scalar(select(PMUser).where(PMUser.username == payload.username))
|
|
if user is None:
|
|
raise AuthError("حساب نمایشی ساخته نشده است؛ سرور را دوباره راهاندازی کنید")
|
|
ctx = await build_context(db, user)
|
|
return {"data": {"token": create_token(user.id), "user": user_payload(ctx)}}
|