mirror of
https://github.com/open-webui/open-webui.git
synced 2026-03-11 17:47:44 -05:00
fix: race condition in signup allows multiple admin accounts (#21631)
The signup_handler function checks has_users() before inserting a new user and assigns the admin role based on that check. With multiple uvicorn workers, concurrent signup requests during first-user registration can all observe an empty user table before any insert completes, causing multiple accounts to receive the admin role. Fix: insert with the default role first, then check user count after the insert. Only promote to admin if this is the only user in the database. This eliminates the TOCTOU window between the check and the insert.
This commit is contained in:
@@ -700,8 +700,9 @@ async def signup_handler(
|
||||
Returns the newly created UserModel.
|
||||
Raises HTTPException on failure.
|
||||
"""
|
||||
has_users = Users.has_users(db=db)
|
||||
role = "admin" if not has_users else request.app.state.config.DEFAULT_USER_ROLE
|
||||
# Insert with default role first to avoid TOCTOU race on first signup.
|
||||
# If has_users() is checked before insert, concurrent requests during
|
||||
# first-user registration can all see an empty table and each get admin.
|
||||
hashed = get_password_hash(password)
|
||||
|
||||
user = Auths.insert_new_auth(
|
||||
@@ -709,12 +710,19 @@ async def signup_handler(
|
||||
password=hashed,
|
||||
name=name,
|
||||
profile_image_url=profile_image_url,
|
||||
role=role,
|
||||
role=request.app.state.config.DEFAULT_USER_ROLE,
|
||||
db=db,
|
||||
)
|
||||
if not user:
|
||||
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
|
||||
|
||||
# Atomically check if this is the only user *after* the insert.
|
||||
# Only the single user present at this point should become admin.
|
||||
if Users.get_num_users(db=db) == 1:
|
||||
Users.update_user_role_by_id(user.id, "admin", db=db)
|
||||
user = Users.get_user_by_id(user.id, db=db)
|
||||
request.app.state.config.ENABLE_SIGNUP = False
|
||||
|
||||
if request.app.state.config.WEBHOOK_URL:
|
||||
await post_webhook(
|
||||
request.app.state.WEBUI_NAME,
|
||||
@@ -727,10 +735,6 @@ async def signup_handler(
|
||||
},
|
||||
)
|
||||
|
||||
if not has_users:
|
||||
# Disable signup after the first user is created
|
||||
request.app.state.config.ENABLE_SIGNUP = False
|
||||
|
||||
apply_default_group_assignment(
|
||||
request.app.state.config.DEFAULT_GROUP_ID,
|
||||
user.id,
|
||||
|
||||
Reference in New Issue
Block a user