feat: Add frontend permission option for user creation and enhance validation error handling
This commit introduces a new `give_frontend_permission` field in the user creation request, allowing admins to create users with frontend access. The frontend has been updated to include validation for required fields and improved error messaging for Pydantic validation errors. Additionally, the backend has been modified to handle the creation of users in the auth database if frontend permission is granted. Documentation has been updated to reflect these changes.
This commit is contained in:
+82
-2
@@ -5,10 +5,11 @@ from __future__ import annotations
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.web.api.auth import get_current_user
|
||||
from src.web.db.session import get_db
|
||||
from src.web.db.session import get_auth_db, get_db
|
||||
from src.web.db.models import User
|
||||
from src.web.schemas.users import (
|
||||
UserCreateRequest,
|
||||
@@ -21,6 +22,15 @@ from src.web.utils.password import hash_password
|
||||
router = APIRouter(prefix="/users", tags=["users"])
|
||||
|
||||
|
||||
def get_auth_db_optional() -> Session | None:
|
||||
"""Get auth database session if available, otherwise return None."""
|
||||
try:
|
||||
return next(get_auth_db())
|
||||
except ValueError:
|
||||
# Auth database not configured
|
||||
return None
|
||||
|
||||
|
||||
def get_current_admin_user(
|
||||
current_user: Annotated[dict, Depends(get_current_user)],
|
||||
db: Session = Depends(get_db),
|
||||
@@ -104,7 +114,11 @@ def create_user(
|
||||
request: UserCreateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
) -> UserResponse:
|
||||
"""Create a new user - admin only."""
|
||||
"""Create a new user - admin only.
|
||||
|
||||
If give_frontend_permission is True, also creates the user in the auth database
|
||||
for frontend access.
|
||||
"""
|
||||
# Check if username already exists
|
||||
existing_user = db.query(User).filter(User.username == request.username).first()
|
||||
if existing_user:
|
||||
@@ -137,6 +151,72 @@ def create_user(
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
|
||||
# If frontend permission is requested, create user in auth database
|
||||
if request.give_frontend_permission:
|
||||
auth_db = get_auth_db_optional()
|
||||
if auth_db is None:
|
||||
# Auth database not configured - this is okay, just continue
|
||||
# The backend user was created successfully
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Check if user with same email already exists in auth db
|
||||
check_result = auth_db.execute(text("""
|
||||
SELECT id FROM users
|
||||
WHERE email = :email
|
||||
"""), {"email": request.email})
|
||||
|
||||
existing_auth = check_result.first()
|
||||
if existing_auth:
|
||||
# User already exists in auth db, skip creation
|
||||
# This is not an error - user might have been created separately
|
||||
pass
|
||||
else:
|
||||
# Insert new user in auth database
|
||||
# Check database dialect for RETURNING support
|
||||
dialect = auth_db.bind.dialect.name if auth_db.bind else 'postgresql'
|
||||
supports_returning = dialect == 'postgresql'
|
||||
|
||||
# Set has_write_access based on admin status
|
||||
# Admins get write access by default, regular users don't
|
||||
has_write_access = request.is_admin
|
||||
|
||||
# Use the same password hash
|
||||
if supports_returning:
|
||||
auth_db.execute(text("""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""), {
|
||||
"email": request.email,
|
||||
"name": request.full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": request.is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
})
|
||||
auth_db.commit()
|
||||
else:
|
||||
# SQLite - insert then select
|
||||
auth_db.execute(text("""
|
||||
INSERT INTO users (email, name, password_hash, is_admin, has_write_access)
|
||||
VALUES (:email, :name, :password_hash, :is_admin, :has_write_access)
|
||||
"""), {
|
||||
"email": request.email,
|
||||
"name": request.full_name,
|
||||
"password_hash": password_hash,
|
||||
"is_admin": request.is_admin,
|
||||
"has_write_access": has_write_access,
|
||||
})
|
||||
auth_db.commit()
|
||||
except Exception as e:
|
||||
# If auth user creation fails, rollback and log but don't fail the whole request
|
||||
# The backend user was already created successfully
|
||||
auth_db.rollback()
|
||||
# In production, you might want to log this to a proper logging system
|
||||
import traceback
|
||||
print(f"Warning: Failed to create auth user: {str(e)}\n{traceback.format_exc()}")
|
||||
finally:
|
||||
auth_db.close()
|
||||
|
||||
return UserResponse.model_validate(user)
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class UserCreateRequest(BaseModel):
|
||||
full_name: str = Field(..., min_length=1, max_length=200, description="Full name (required)")
|
||||
is_active: bool = True
|
||||
is_admin: bool = False
|
||||
give_frontend_permission: bool = Field(False, description="Create user in auth database for frontend access")
|
||||
|
||||
|
||||
class UserUpdateRequest(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user