88from app .api .deps import (
99 CurrentUser ,
1010 SessionDep ,
11- get_current_active_superuser ,
11+ get_current_user_manager ,
1212)
1313from app .core .config import settings
1414from app .core .security import get_password_hash , verify_password
1515from app .models import (
16+ AuditAction ,
17+ AuditLogsPublic ,
1618 Item ,
1719 Message ,
1820 UpdatePassword ,
1921 User ,
2022 UserCreate ,
2123 UserPublic ,
2224 UserRegister ,
25+ UserRole ,
2326 UsersPublic ,
2427 UserUpdate ,
2528 UserUpdateMe ,
3134
3235@router .get (
3336 "/" ,
34- dependencies = [Depends (get_current_active_superuser )],
37+ dependencies = [Depends (get_current_user_manager )],
3538 response_model = UsersPublic ,
3639)
3740def read_users (session : SessionDep , skip : int = 0 , limit : int = 100 ) -> Any :
@@ -51,11 +54,16 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
5154
5255
5356@router .post (
54- "/" , dependencies = [Depends (get_current_active_superuser )], response_model = UserPublic
57+ "/" ,
58+ dependencies = [Depends (get_current_user_manager )],
59+ response_model = UserPublic ,
5560)
56- def create_user (* , session : SessionDep , user_in : UserCreate ) -> Any :
61+ def create_user (
62+ * , session : SessionDep , user_in : UserCreate , current_user : CurrentUser
63+ ) -> Any :
5764 """
58- Create new user.
65+ Create new user. Requires email and role at minimum.
66+ Password is optional (generated automatically for passwordless flow).
5967 """
6068 user = crud .get_user_by_email (session = session , email = user_in .email )
6169 if user :
@@ -67,13 +75,23 @@ def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
6775 user = crud .create_user (session = session , user_create = user_in )
6876 if settings .emails_enabled and user_in .email :
6977 email_data = generate_new_account_email (
70- email_to = user_in .email , username = user_in .email , password = user_in .password
78+ email_to = user_in .email ,
79+ username = user_in .email ,
80+ password = user_in .password or "" ,
7181 )
7282 send_email (
7383 email_to = user_in .email ,
7484 subject = email_data .subject ,
7585 html_content = email_data .html_content ,
7686 )
87+
88+ crud .create_audit_log (
89+ session = session ,
90+ action = AuditAction .created ,
91+ target_user_id = user .id ,
92+ performed_by_id = current_user .id ,
93+ changes = f"User created with role={ user_in .role .value } " ,
94+ )
7795 return user
7896
7997
@@ -158,6 +176,19 @@ def register_user(session: SessionDep, user_in: UserRegister) -> Any:
158176 return user
159177
160178
179+ @router .get (
180+ "/audit-log" ,
181+ dependencies = [Depends (get_current_user_manager )],
182+ response_model = AuditLogsPublic ,
183+ )
184+ def read_audit_logs (session : SessionDep , skip : int = 0 , limit : int = 100 ) -> Any :
185+ """
186+ Retrieve user audit logs.
187+ """
188+ logs , count = crud .get_audit_logs (session = session , skip = skip , limit = limit )
189+ return AuditLogsPublic (data = logs , count = count )
190+
191+
161192@router .get ("/{user_id}" , response_model = UserPublic )
162193def read_user_by_id (
163194 user_id : uuid .UUID , session : SessionDep , current_user : CurrentUser
@@ -180,17 +211,18 @@ def read_user_by_id(
180211
181212@router .patch (
182213 "/{user_id}" ,
183- dependencies = [Depends (get_current_active_superuser )],
214+ dependencies = [Depends (get_current_user_manager )],
184215 response_model = UserPublic ,
185216)
186217def update_user (
187218 * ,
188219 session : SessionDep ,
189220 user_id : uuid .UUID ,
190221 user_in : UserUpdate ,
222+ current_user : CurrentUser ,
191223) -> Any :
192224 """
193- Update a user.
225+ Update a user (role, active status, etc.) .
194226 """
195227
196228 db_user = session .get (User , user_id )
@@ -206,16 +238,49 @@ def update_user(
206238 status_code = 409 , detail = "User with this email already exists"
207239 )
208240
241+ changes_parts = []
242+ user_data = user_in .model_dump (exclude_unset = True )
243+ if "role" in user_data and user_data ["role" ] is not None :
244+ changes_parts .append (f"role: { db_user .role .value } -> { user_data ['role' ]} " )
245+ if "is_active" in user_data and user_data ["is_active" ] is not None :
246+ changes_parts .append (
247+ f"is_active: { db_user .is_active } -> { user_data ['is_active' ]} "
248+ )
249+ if "email" in user_data and user_data ["email" ] is not None :
250+ changes_parts .append (f"email: { db_user .email } -> { user_data ['email' ]} " )
251+ if "full_name" in user_data :
252+ changes_parts .append (
253+ f"full_name: { db_user .full_name } -> { user_data ['full_name' ]} "
254+ )
255+
256+ is_deactivation = (
257+ "is_active" in user_data
258+ and user_data ["is_active" ] is False
259+ and db_user .is_active is True
260+ )
261+
209262 db_user = crud .update_user (session = session , db_user = db_user , user_in = user_in )
263+
264+ audit_action = AuditAction .deactivated if is_deactivation else AuditAction .updated
265+ crud .create_audit_log (
266+ session = session ,
267+ action = audit_action ,
268+ target_user_id = db_user .id ,
269+ performed_by_id = current_user .id ,
270+ changes = "; " .join (changes_parts ) if changes_parts else "No changes" ,
271+ )
210272 return db_user
211273
212274
213- @router .delete ("/{user_id}" , dependencies = [Depends (get_current_active_superuser )])
275+ @router .delete (
276+ "/{user_id}" ,
277+ dependencies = [Depends (get_current_user_manager )],
278+ )
214279def delete_user (
215280 session : SessionDep , current_user : CurrentUser , user_id : uuid .UUID
216281) -> Message :
217282 """
218- Delete a user.
283+ Delete a user. Only a Super Admin can delete another Super Admin.
219284 """
220285 user = session .get (User , user_id )
221286 if not user :
@@ -224,6 +289,11 @@ def delete_user(
224289 raise HTTPException (
225290 status_code = 403 , detail = "Super users are not allowed to delete themselves"
226291 )
292+ if user .role == UserRole .super_admin and current_user .role != UserRole .super_admin :
293+ raise HTTPException (
294+ status_code = 403 ,
295+ detail = "Only a Super Admin can delete another Super Admin" ,
296+ )
227297 statement = delete (Item ).where (col (Item .owner_id ) == user_id )
228298 session .exec (statement )
229299 session .delete (user )
0 commit comments