1717
1818
1919def login_local_user (email : str , password : str ) -> tuple [User | None , str | None ]:
20+ if not current_app .config ["LOCAL_AUTH_ENABLED" ]:
21+ return None , "local_auth_disabled"
2022 normalized_email = email .strip ().lower ()
2123 user = User .query .filter_by (email = normalized_email ).one_or_none ()
2224 if user is None or not user .password_hash :
@@ -152,10 +154,19 @@ def _complete_login(user: User, provider: str) -> tuple[User | None, str | None]
152154 return user , None
153155
154156
155- def create_local_user (email : str , password : str , display_name : str | None , roles : list [str ]) -> tuple [dict , int ]:
157+ def create_local_user (
158+ email : str ,
159+ password : str ,
160+ display_name : str | None ,
161+ roles : list [str ],
162+ first_name : str | None = None ,
163+ last_name : str | None = None ,
164+ ) -> tuple [dict , int ]:
165+ display_name = _display_name (display_name , first_name , last_name , email )
156166 user , error = register_local_user (email , password , display_name )
157167 if error :
158168 return {"error" : error }, 400
169+ user .external_id = _profile_external_id (user .external_id , first_name , last_name )
159170 for role in roles :
160171 if role :
161172 _ensure_role (user , role )
@@ -279,18 +290,29 @@ def set_user_status(user_id: str, status: str) -> tuple[dict, int]:
279290 return {"user_id" : str (user .id ), "status" : status }, 200
280291
281292
282- def update_user_profile (user_id : str , display_name : str | None , external_id : str | None ) -> tuple [dict , int ]:
293+ def update_user_profile (
294+ user_id : str ,
295+ display_name : str | None ,
296+ external_id : str | None ,
297+ first_name : str | None = None ,
298+ last_name : str | None = None ,
299+ ) -> tuple [dict , int ]:
283300 user , error_response = _user_from_id (user_id )
284301 if error_response :
285302 return error_response
286- user .display_name = display_name or user .email
287- user .external_id = external_id or None
303+ user .display_name = _display_name ( display_name , first_name , last_name , user .email )
304+ user .external_id = _profile_external_id ( external_id , first_name , last_name )
288305 record_audit_event (
289306 event_type = "user.profile_updated" ,
290307 outcome = "success" ,
291308 target_type = "user" ,
292309 target_id = user .id ,
293- metadata = {"display_name" : user .display_name , "external_id" : user .external_id },
310+ metadata = {
311+ "display_name" : user .display_name ,
312+ "external_id" : external_id ,
313+ "first_name" : (first_name or "" ).strip (),
314+ "last_name" : (last_name or "" ).strip (),
315+ },
294316 )
295317 db .session .commit ()
296318 return {"user_id" : str (user .id )}, 200
@@ -367,11 +389,16 @@ def list_users_with_roles() -> list[dict]:
367389 .all ()
368390 )
369391 servers = accessible_servers_for_user (user .id )
392+ first_name , last_name , directory_id = _profile_parts (user .external_id )
370393 result .append (
371394 {
372395 "id" : str (user .id ),
373396 "email" : user .email ,
374397 "display_name" : user .display_name ,
398+ "first_name" : first_name ,
399+ "last_name" : last_name ,
400+ "directory_id" : directory_id ,
401+ "initials" : _initials (first_name , last_name , user .display_name , user .email ),
375402 "status" : user .status ,
376403 "auth_provider" : user .auth_provider ,
377404 "external_id" : user .external_id ,
@@ -392,6 +419,52 @@ def list_users_with_roles() -> list[dict]:
392419 return result
393420
394421
422+ def _display_name (display_name : str | None , first_name : str | None , last_name : str | None , email : str ) -> str :
423+ explicit = (display_name or "" ).strip ()
424+ if explicit :
425+ return explicit
426+ full_name = " " .join (part for part in [(first_name or "" ).strip (), (last_name or "" ).strip ()] if part )
427+ return full_name or email
428+
429+
430+ def _profile_external_id (external_id : str | None , first_name : str | None , last_name : str | None ) -> str | None :
431+ directory_id = (external_id or "" ).strip ()
432+ parts = []
433+ if directory_id :
434+ parts .append (directory_id )
435+ first_name = (first_name or "" ).strip ()
436+ last_name = (last_name or "" ).strip ()
437+ if first_name :
438+ parts .append (f"first_name={ first_name } " )
439+ if last_name :
440+ parts .append (f"last_name={ last_name } " )
441+ return "|" .join (parts ) or None
442+
443+
444+ def _profile_parts (external_id : str | None ) -> tuple [str , str , str | None ]:
445+ first_name = ""
446+ last_name = ""
447+ directory_parts = []
448+ for part in (external_id or "" ).split ("|" ):
449+ if part .startswith ("first_name=" ):
450+ first_name = part .removeprefix ("first_name=" )
451+ elif part .startswith ("last_name=" ):
452+ last_name = part .removeprefix ("last_name=" )
453+ elif part :
454+ directory_parts .append (part )
455+ return first_name , last_name , "|" .join (directory_parts ) or None
456+
457+
458+ def _initials (first_name : str , last_name : str , display_name : str | None , email : str ) -> str :
459+ if first_name or last_name :
460+ return f"{ first_name [:1 ]} { last_name [:1 ]} " .upper () or "U"
461+ name = (display_name or email or "U" ).strip ()
462+ parts = [part for part in name .split () if part ]
463+ if len (parts ) >= 2 :
464+ return f"{ parts [0 ][:1 ]} { parts [- 1 ][:1 ]} " .upper ()
465+ return name [:2 ].upper ()
466+
467+
395468def _domain_allowed (email : str ) -> bool :
396469 if "@" not in email :
397470 return False
0 commit comments