Issue #2: The profile route used a custom role-based check instead of the permission-based pattern used everywhere else. Changes: - Add MANAGE_OWN_PROFILE permission to backend Permission enum - Add permission to ROLE_REGULAR role definition - Update profile routes to use require_permission(MANAGE_OWN_PROFILE) - Remove custom require_regular_user dependency - Update frontend Permission constant and profile page - Update invites page to use permission instead of role check - Update profile tests with proper permission mocking This ensures consistent authorization patterns across all routes.
79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
"""Profile routes for user contact details."""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from auth import require_permission
|
|
from database import get_db
|
|
from models import Permission, User
|
|
from schemas import ProfileResponse, ProfileUpdate
|
|
from validation import validate_profile_fields
|
|
|
|
router = APIRouter(prefix="/api/profile", tags=["profile"])
|
|
|
|
|
|
async def get_godfather_email(db: AsyncSession, godfather_id: int | None) -> str | None:
|
|
"""Get the email of a godfather user by ID."""
|
|
if not godfather_id:
|
|
return None
|
|
result = await db.execute(select(User.email).where(User.id == godfather_id))
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
@router.get("", response_model=ProfileResponse)
|
|
async def get_profile(
|
|
current_user: User = Depends(require_permission(Permission.MANAGE_OWN_PROFILE)),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> ProfileResponse:
|
|
"""Get the current user's profile (contact details and godfather)."""
|
|
godfather_email = await get_godfather_email(db, current_user.godfather_id)
|
|
|
|
return ProfileResponse(
|
|
contact_email=current_user.contact_email,
|
|
telegram=current_user.telegram,
|
|
signal=current_user.signal,
|
|
nostr_npub=current_user.nostr_npub,
|
|
godfather_email=godfather_email,
|
|
)
|
|
|
|
|
|
@router.put("", response_model=ProfileResponse)
|
|
async def update_profile(
|
|
data: ProfileUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(require_permission(Permission.MANAGE_OWN_PROFILE)),
|
|
) -> ProfileResponse:
|
|
"""Update the current user's profile (contact details)."""
|
|
# Validate all fields
|
|
errors = validate_profile_fields(
|
|
contact_email=data.contact_email,
|
|
telegram=data.telegram,
|
|
signal=data.signal,
|
|
nostr_npub=data.nostr_npub,
|
|
)
|
|
|
|
if errors:
|
|
raise HTTPException(
|
|
status_code=422,
|
|
detail={"field_errors": errors},
|
|
)
|
|
|
|
# Update fields
|
|
current_user.contact_email = data.contact_email
|
|
current_user.telegram = data.telegram
|
|
current_user.signal = data.signal
|
|
current_user.nostr_npub = data.nostr_npub
|
|
|
|
await db.commit()
|
|
await db.refresh(current_user)
|
|
|
|
godfather_email = await get_godfather_email(db, current_user.godfather_id)
|
|
|
|
return ProfileResponse(
|
|
contact_email=current_user.contact_email,
|
|
telegram=current_user.telegram,
|
|
signal=current_user.signal,
|
|
nostr_npub=current_user.nostr_npub,
|
|
godfather_email=godfather_email,
|
|
)
|