refac: async db
This commit is contained in:
@@ -30,8 +30,8 @@ from open_webui.config import OAUTH_PROVIDERS
|
||||
from open_webui.env import SCIM_AUTH_PROVIDER
|
||||
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from open_webui.internal.db import get_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -326,18 +326,18 @@ def get_scim_provider() -> str:
|
||||
return SCIM_AUTH_PROVIDER
|
||||
|
||||
|
||||
def find_user_by_external_id(external_id: str, db=None) -> Optional[UserModel]:
|
||||
async def find_user_by_external_id(external_id: str, db=None) -> Optional[UserModel]:
|
||||
"""Find a user by SCIM externalId, falling back to OAuth sub match."""
|
||||
provider = get_scim_provider()
|
||||
user = Users.get_user_by_scim_external_id(provider, external_id, db=db)
|
||||
user = await Users.get_user_by_scim_external_id(provider, external_id, db=db)
|
||||
if user:
|
||||
return user
|
||||
|
||||
# Fallback: check if externalId matches an existing OAuth sub (account linking)
|
||||
return Users.get_user_by_oauth_sub(provider, external_id, db=db)
|
||||
return await Users.get_user_by_oauth_sub(provider, external_id, db=db)
|
||||
|
||||
|
||||
def user_to_scim(user: UserModel, request: Request, db=None) -> SCIMUser:
|
||||
async def user_to_scim(user: UserModel, request: Request, db=None) -> SCIMUser:
|
||||
"""Convert internal User model to SCIM User"""
|
||||
# Parse display name into name components
|
||||
name_parts = user.name.split(' ', 1) if user.name else ['', '']
|
||||
@@ -345,7 +345,7 @@ def user_to_scim(user: UserModel, request: Request, db=None) -> SCIMUser:
|
||||
family_name = name_parts[1] if len(name_parts) > 1 else ''
|
||||
|
||||
# Get user's groups
|
||||
user_groups = Groups.get_groups_by_member_id(user.id, db=db)
|
||||
user_groups = await Groups.get_groups_by_member_id(user.id, db=db)
|
||||
groups = [
|
||||
{
|
||||
'value': group.id,
|
||||
@@ -379,12 +379,12 @@ def user_to_scim(user: UserModel, request: Request, db=None) -> SCIMUser:
|
||||
)
|
||||
|
||||
|
||||
def group_to_scim(group: GroupModel, request: Request, db=None) -> SCIMGroup:
|
||||
async def group_to_scim(group: GroupModel, request: Request, db=None) -> SCIMGroup:
|
||||
"""Convert internal Group model to SCIM Group"""
|
||||
member_ids = Groups.get_group_user_ids_by_id(group.id, db) or []
|
||||
member_ids = await Groups.get_group_user_ids_by_id(group.id, db) or []
|
||||
|
||||
# Batch-fetch all users to avoid N+1 queries
|
||||
users = Users.get_users_by_user_ids(member_ids, db=db) if member_ids else []
|
||||
users = await Users.get_users_by_user_ids(member_ids, db=db) if member_ids else []
|
||||
members = [
|
||||
SCIMGroupMember(
|
||||
value=user.id,
|
||||
@@ -512,7 +512,7 @@ async def get_users(
|
||||
count: int = Query(20),
|
||||
filter: Optional[str] = None,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""List SCIM Users"""
|
||||
# Clamp per SCIM 2.0 spec (RFC 7644 §3.4.2.4):
|
||||
@@ -527,25 +527,25 @@ async def get_users(
|
||||
# Simple filter parsing - supports userName eq, externalId eq
|
||||
if 'userName eq' in filter:
|
||||
email = filter.split('"')[1]
|
||||
user = Users.get_user_by_email(email, db=db)
|
||||
user = await Users.get_user_by_email(email, db=db)
|
||||
users_list = [user] if user else []
|
||||
total = 1 if user else 0
|
||||
elif 'externalId eq' in filter:
|
||||
external_id = filter.split('"')[1]
|
||||
user = find_user_by_external_id(external_id, db=db)
|
||||
user = await find_user_by_external_id(external_id, db=db)
|
||||
users_list = [user] if user else []
|
||||
total = 1 if user else 0
|
||||
else:
|
||||
response = Users.get_users(skip=skip, limit=limit, db=db)
|
||||
response = await Users.get_users(skip=skip, limit=limit, db=db)
|
||||
users_list = response['users']
|
||||
total = response['total']
|
||||
else:
|
||||
response = Users.get_users(skip=skip, limit=limit, db=db)
|
||||
response = await Users.get_users(skip=skip, limit=limit, db=db)
|
||||
users_list = response['users']
|
||||
total = response['total']
|
||||
|
||||
# Convert to SCIM format
|
||||
scim_users = [user_to_scim(user, request, db=db) for user in users_list]
|
||||
scim_users = [await user_to_scim(user, request, db=db) for user in users_list]
|
||||
|
||||
return SCIMListResponse(
|
||||
totalResults=total,
|
||||
@@ -560,14 +560,14 @@ async def get_user(
|
||||
user_id: str,
|
||||
request: Request,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Get SCIM User by ID"""
|
||||
user = Users.get_user_by_id(user_id, db=db)
|
||||
user = await Users.get_user_by_id(user_id, db=db)
|
||||
if not user:
|
||||
return scim_error(status_code=status.HTTP_404_NOT_FOUND, detail=f'User {user_id} not found')
|
||||
|
||||
return user_to_scim(user, request, db=db)
|
||||
return await user_to_scim(user, request, db=db)
|
||||
|
||||
|
||||
@router.post('/Users', response_model=SCIMUser, status_code=status.HTTP_201_CREATED)
|
||||
@@ -575,12 +575,12 @@ async def create_user(
|
||||
request: Request,
|
||||
user_data: SCIMUserCreateRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Create SCIM User"""
|
||||
# Check for duplicate by externalId
|
||||
if user_data.externalId:
|
||||
existing_user = find_user_by_external_id(user_data.externalId, db=db)
|
||||
existing_user = await find_user_by_external_id(user_data.externalId, db=db)
|
||||
if existing_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
@@ -596,7 +596,7 @@ async def create_user(
|
||||
email = email.lower()
|
||||
|
||||
# Check for duplicate by email
|
||||
existing_user = Users.get_user_by_email(email, db=db)
|
||||
existing_user = await Users.get_user_by_email(email, db=db)
|
||||
if existing_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
@@ -619,7 +619,7 @@ async def create_user(
|
||||
if user_data.photos and len(user_data.photos) > 0:
|
||||
profile_image = user_data.photos[0].value
|
||||
|
||||
new_user = Users.insert_new_user(
|
||||
new_user = await Users.insert_new_user(
|
||||
id=user_id,
|
||||
name=name,
|
||||
email=email,
|
||||
@@ -637,10 +637,10 @@ async def create_user(
|
||||
# Store externalId in the scim field
|
||||
if user_data.externalId:
|
||||
provider = get_scim_provider()
|
||||
Users.update_user_scim_by_id(user_id, provider, user_data.externalId, db=db)
|
||||
new_user = Users.get_user_by_id(user_id, db=db)
|
||||
await Users.update_user_scim_by_id(user_id, provider, user_data.externalId, db=db)
|
||||
new_user = await Users.get_user_by_id(user_id, db=db)
|
||||
|
||||
return user_to_scim(new_user, request, db=db)
|
||||
return await user_to_scim(new_user, request, db=db)
|
||||
|
||||
|
||||
@router.put('/Users/{user_id}', response_model=SCIMUser)
|
||||
@@ -649,10 +649,10 @@ async def update_user(
|
||||
request: Request,
|
||||
user_data: SCIMUserUpdateRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Update SCIM User (full update)"""
|
||||
user = Users.get_user_by_id(user_id, db=db)
|
||||
user = await Users.get_user_by_id(user_id, db=db)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -682,7 +682,7 @@ async def update_user(
|
||||
if user_data.photos and len(user_data.photos) > 0:
|
||||
update_data['profile_image_url'] = user_data.photos[0].value
|
||||
|
||||
updated_user = Users.update_user_by_id(user_id, update_data, db=db)
|
||||
updated_user = await Users.update_user_by_id(user_id, update_data, db=db)
|
||||
if not updated_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -692,10 +692,10 @@ async def update_user(
|
||||
# Update externalId in the scim field
|
||||
if user_data.externalId:
|
||||
provider = get_scim_provider()
|
||||
Users.update_user_scim_by_id(user_id, provider, user_data.externalId, db=db)
|
||||
updated_user = Users.get_user_by_id(user_id, db=db)
|
||||
await Users.update_user_scim_by_id(user_id, provider, user_data.externalId, db=db)
|
||||
updated_user = await Users.get_user_by_id(user_id, db=db)
|
||||
|
||||
return user_to_scim(updated_user, request, db=db)
|
||||
return await user_to_scim(updated_user, request, db=db)
|
||||
|
||||
|
||||
@router.patch('/Users/{user_id}', response_model=SCIMUser)
|
||||
@@ -704,10 +704,10 @@ async def patch_user(
|
||||
request: Request,
|
||||
patch_data: SCIMPatchRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Update SCIM User (partial update)"""
|
||||
user = Users.get_user_by_id(user_id, db=db)
|
||||
user = await Users.get_user_by_id(user_id, db=db)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -734,11 +734,11 @@ async def patch_user(
|
||||
update_data['name'] = value
|
||||
elif path == 'externalId':
|
||||
provider = get_scim_provider()
|
||||
Users.update_user_scim_by_id(user_id, provider, value, db=db)
|
||||
await Users.update_user_scim_by_id(user_id, provider, value, db=db)
|
||||
|
||||
# Update user
|
||||
if update_data:
|
||||
updated_user = Users.update_user_by_id(user_id, update_data, db=db)
|
||||
updated_user = await Users.update_user_by_id(user_id, update_data, db=db)
|
||||
if not updated_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -747,7 +747,7 @@ async def patch_user(
|
||||
else:
|
||||
updated_user = user
|
||||
|
||||
return user_to_scim(updated_user, request, db=db)
|
||||
return await user_to_scim(updated_user, request, db=db)
|
||||
|
||||
|
||||
@router.delete('/Users/{user_id}', status_code=status.HTTP_204_NO_CONTENT)
|
||||
@@ -755,17 +755,17 @@ async def delete_user(
|
||||
user_id: str,
|
||||
request: Request,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Delete SCIM User"""
|
||||
user = Users.get_user_by_id(user_id, db=db)
|
||||
user = await Users.get_user_by_id(user_id, db=db)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f'User {user_id} not found',
|
||||
)
|
||||
|
||||
success = Users.delete_user_by_id(user_id, db=db)
|
||||
success = await Users.delete_user_by_id(user_id, db=db)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -783,7 +783,7 @@ async def get_groups(
|
||||
count: int = Query(20),
|
||||
filter: Optional[str] = None,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""List SCIM Groups"""
|
||||
# Clamp per SCIM 2.0 spec (RFC 7644 §3.4.2.4):
|
||||
@@ -795,13 +795,13 @@ async def get_groups(
|
||||
if filter:
|
||||
if 'displayName eq' in filter:
|
||||
display_name = filter.split('"')[1]
|
||||
group = Groups.get_group_by_name(display_name, db=db)
|
||||
group = await Groups.get_group_by_name(display_name, db=db)
|
||||
groups_list = [group] if group else []
|
||||
else:
|
||||
# Unrecognized filter — fall back to all groups
|
||||
groups_list = Groups.get_all_groups(db=db)
|
||||
groups_list = await Groups.get_all_groups(db=db)
|
||||
else:
|
||||
groups_list = Groups.get_all_groups(db=db)
|
||||
groups_list = await Groups.get_all_groups(db=db)
|
||||
|
||||
# Apply pagination
|
||||
total = len(groups_list)
|
||||
@@ -810,7 +810,7 @@ async def get_groups(
|
||||
paginated_groups = groups_list[start:end]
|
||||
|
||||
# Convert to SCIM format
|
||||
scim_groups = [group_to_scim(group, request, db=db) for group in paginated_groups]
|
||||
scim_groups = [await group_to_scim(group, request, db=db) for group in paginated_groups]
|
||||
|
||||
return SCIMListResponse(
|
||||
totalResults=total,
|
||||
@@ -825,17 +825,17 @@ async def get_group(
|
||||
group_id: str,
|
||||
request: Request,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Get SCIM Group by ID"""
|
||||
group = Groups.get_group_by_id(group_id, db=db)
|
||||
group = await Groups.get_group_by_id(group_id, db=db)
|
||||
if not group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f'Group {group_id} not found',
|
||||
)
|
||||
|
||||
return group_to_scim(group, request, db=db)
|
||||
return await group_to_scim(group, request, db=db)
|
||||
|
||||
|
||||
@router.post('/Groups', response_model=SCIMGroup, status_code=status.HTTP_201_CREATED)
|
||||
@@ -843,7 +843,7 @@ async def create_group(
|
||||
request: Request,
|
||||
group_data: SCIMGroupCreateRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Create SCIM Group"""
|
||||
# Extract member IDs
|
||||
@@ -861,14 +861,14 @@ async def create_group(
|
||||
)
|
||||
|
||||
# Need to get the creating user's ID - we'll use the first admin
|
||||
admin_user = Users.get_super_admin_user(db=db)
|
||||
admin_user = await Users.get_super_admin_user(db=db)
|
||||
if not admin_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail='No admin user found',
|
||||
)
|
||||
|
||||
new_group = Groups.insert_new_group(admin_user.id, form, db=db)
|
||||
new_group = await Groups.insert_new_group(admin_user.id, form, db=db)
|
||||
if not new_group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -884,12 +884,12 @@ async def create_group(
|
||||
description=new_group.description,
|
||||
)
|
||||
|
||||
Groups.update_group_by_id(new_group.id, update_form, db=db)
|
||||
Groups.set_group_user_ids_by_id(new_group.id, member_ids, db=db)
|
||||
await Groups.update_group_by_id(new_group.id, update_form, db=db)
|
||||
await Groups.set_group_user_ids_by_id(new_group.id, member_ids, db=db)
|
||||
|
||||
new_group = Groups.get_group_by_id(new_group.id, db=db)
|
||||
new_group = await Groups.get_group_by_id(new_group.id, db=db)
|
||||
|
||||
return group_to_scim(new_group, request, db=db)
|
||||
return await group_to_scim(new_group, request, db=db)
|
||||
|
||||
|
||||
@router.put('/Groups/{group_id}', response_model=SCIMGroup)
|
||||
@@ -898,10 +898,10 @@ async def update_group(
|
||||
request: Request,
|
||||
group_data: SCIMGroupUpdateRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Update SCIM Group (full update)"""
|
||||
group = Groups.get_group_by_id(group_id, db=db)
|
||||
group = await Groups.get_group_by_id(group_id, db=db)
|
||||
if not group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -919,17 +919,17 @@ async def update_group(
|
||||
# Handle members if provided
|
||||
if group_data.members is not None:
|
||||
member_ids = [member.value for member in group_data.members]
|
||||
Groups.set_group_user_ids_by_id(group_id, member_ids, db=db)
|
||||
await Groups.set_group_user_ids_by_id(group_id, member_ids, db=db)
|
||||
|
||||
# Update group
|
||||
updated_group = Groups.update_group_by_id(group_id, update_form, db=db)
|
||||
updated_group = await Groups.update_group_by_id(group_id, update_form, db=db)
|
||||
if not updated_group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail='Failed to update group',
|
||||
)
|
||||
|
||||
return group_to_scim(updated_group, request, db=db)
|
||||
return await group_to_scim(updated_group, request, db=db)
|
||||
|
||||
|
||||
@router.patch('/Groups/{group_id}', response_model=SCIMGroup)
|
||||
@@ -938,10 +938,10 @@ async def patch_group(
|
||||
request: Request,
|
||||
patch_data: SCIMPatchRequest,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Update SCIM Group (partial update)"""
|
||||
group = Groups.get_group_by_id(group_id, db=db)
|
||||
group = await Groups.get_group_by_id(group_id, db=db)
|
||||
if not group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
@@ -965,7 +965,7 @@ async def patch_group(
|
||||
update_form.name = value
|
||||
elif path == 'members':
|
||||
# Replace all members
|
||||
Groups.set_group_user_ids_by_id(group_id, [member['value'] for member in value], db=db)
|
||||
await Groups.set_group_user_ids_by_id(group_id, [member['value'] for member in value], db=db)
|
||||
|
||||
elif op == 'add':
|
||||
if path == 'members':
|
||||
@@ -973,22 +973,22 @@ async def patch_group(
|
||||
if isinstance(value, list):
|
||||
for member in value:
|
||||
if isinstance(member, dict) and 'value' in member:
|
||||
Groups.add_users_to_group(group_id, [member['value']], db=db)
|
||||
await Groups.add_users_to_group(group_id, [member['value']], db=db)
|
||||
elif op == 'remove':
|
||||
if path and path.startswith('members[value eq'):
|
||||
# Remove specific member
|
||||
member_id = path.split('"')[1]
|
||||
Groups.remove_users_from_group(group_id, [member_id], db=db)
|
||||
await Groups.remove_users_from_group(group_id, [member_id], db=db)
|
||||
|
||||
# Update group
|
||||
updated_group = Groups.update_group_by_id(group_id, update_form, db=db)
|
||||
updated_group = await Groups.update_group_by_id(group_id, update_form, db=db)
|
||||
if not updated_group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail='Failed to update group',
|
||||
)
|
||||
|
||||
return group_to_scim(updated_group, request, db=db)
|
||||
return await group_to_scim(updated_group, request, db=db)
|
||||
|
||||
|
||||
@router.delete('/Groups/{group_id}', status_code=status.HTTP_204_NO_CONTENT)
|
||||
@@ -996,17 +996,17 @@ async def delete_group(
|
||||
group_id: str,
|
||||
request: Request,
|
||||
_: bool = Depends(get_scim_auth),
|
||||
db: Session = Depends(get_session),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""Delete SCIM Group"""
|
||||
group = Groups.get_group_by_id(group_id, db=db)
|
||||
group = await Groups.get_group_by_id(group_id, db=db)
|
||||
if not group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f'Group {group_id} not found',
|
||||
)
|
||||
|
||||
success = Groups.delete_group_by_id(group_id, db=db)
|
||||
success = await Groups.delete_group_by_id(group_id, db=db)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
|
||||
Reference in New Issue
Block a user