From 61ea25840ce88a0b8d64e332ab57cc124a3c7c21 Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Mon, 30 Jun 2025 14:11:02 +0200 Subject: [PATCH 1/5] 4856: Ensured that role names from OIDC is kept --- backend/open_webui/utils/auth.py | 2 +- backend/open_webui/utils/oauth.py | 9 ++++++--- .../components/admin/Users/UserList/EditUserModal.svelte | 8 +++++--- src/routes/(app)/+layout.svelte | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 0384d6fcb9be..24086e3042da 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -488,7 +488,7 @@ async def get_current_user_by_api_key(request, api_key: str): return user -VERIFIED_USER_ROLES = {'user', 'admin'} +VERIFIED_USER_ROLES = {'user', 'admin', 'builder', 'local-admin'} def get_verified_user(user=Depends(get_current_user)): diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 0cbfcb2ebf76..b28216bd7f64 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -1518,9 +1518,12 @@ async def get_user_role(self, user, user_data): matched = False for allowed_role in oauth_allowed_roles: if allowed_role == '*' or allowed_role in oauth_roles: - log.debug('Assigned user the user role') - role = 'user' - matched = True + log.debug(f'Using first role from OAuth: {oauth_roles[0]}') + first_role = oauth_roles[0] + if first_role == "end-user": + role = 'user' + else: + role = first_role break for admin_role in oauth_admin_roles: if admin_role in oauth_roles: diff --git a/src/lib/components/admin/Users/UserList/EditUserModal.svelte b/src/lib/components/admin/Users/UserList/EditUserModal.svelte index 99352feb7e2c..adc387a50503 100644 --- a/src/lib/components/admin/Users/UserList/EditUserModal.svelte +++ b/src/lib/components/admin/Users/UserList/EditUserModal.svelte @@ -145,9 +145,11 @@ disabled={_user.id == sessionUser.id} required > - - - + + + + + diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 71bbd80ec731..8c99312e7716 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -456,7 +456,7 @@
- {#if !['user', 'admin'].includes($user?.role)} + {#if ['pending'].includes($user?.role)} {:else} {#if localDBChats.length > 0} From 59228f6b81a80318b1a4fb1d073cf375a007e68d Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Mon, 13 Oct 2025 11:13:59 +0200 Subject: [PATCH 2/5] 4856: Fixed role limitations in layout mount --- src/routes/(app)/+layout.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 8c99312e7716..d27aac417a70 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -248,7 +248,7 @@ await gotoAuth(); return; } - if (!['user', 'admin'].includes($user?.role)) { + if (['pending'].includes($user?.role)) { return; } From 44c222c7ee9172fccba3bca5c5b9d3c914fc3786 Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Mon, 13 Oct 2025 13:47:58 +0200 Subject: [PATCH 3/5] 4856: Fixed roles names in config load --- backend/open_webui/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 55613ac83d47..c5c9feac21a1 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -2273,7 +2273,7 @@ async def get_app_config(request: Request): else {} ), } - if user is not None and (user.role in ['admin', 'user']) + if user is not None and (user.role in ['admin', 'user', 'builder', 'local-admin']) else { **( { From bc14214bdf14ecca1ddf2f83d8b260166ea99616 Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Fri, 5 Dec 2025 13:31:23 +0100 Subject: [PATCH 4/5] 4856: Ensure models list is filter for builders --- backend/open_webui/utils/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/models.py b/backend/open_webui/utils/models.py index 79ea1030a2f7..37499e3dc910 100644 --- a/backend/open_webui/utils/models.py +++ b/backend/open_webui/utils/models.py @@ -475,7 +475,10 @@ async def check_model_access(user, model, model_info=None, db=None): async def get_filtered_models(models, user, db=None): # Filter out models that the user does not have access to if ( - user.role == 'user' or (user.role == 'admin' and not BYPASS_ADMIN_ACCESS_CONTROL) + user.role == 'user' + or user.role == 'builder' + or (user.role == 'admin' and not BYPASS_ADMIN_ACCESS_CONTROL) + ) and not BYPASS_MODEL_ACCESS_CONTROL: model_infos = {} for model in models: From 31424ea35df81e94fde1c878f794031635c7df3c Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Fri, 5 Dec 2025 14:40:03 +0100 Subject: [PATCH 5/5] 4856: Ensure build role is checked on same level as user --- backend/open_webui/routers/chats.py | 2 +- backend/open_webui/routers/ollama.py | 6 +++--- backend/open_webui/routers/openai.py | 2 +- backend/open_webui/utils/chat.py | 2 +- backend/open_webui/utils/embeddings.py | 2 +- backend/open_webui/utils/oauth.py | 1 + 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index e618eb3f066e..8a68f9a1db42 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -682,7 +682,7 @@ async def delete_all_user_chats( user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session), ): - if user.role == 'user' and not await has_permission(user.id, 'chat.delete', await Config.get('user.permissions')): + if (user.role == 'user' or user.role == 'builder') and not await has_permission(user.id, 'chat.delete', await Config.get('user.permissions')): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index b91aed2ac491..6d7bfe850ada 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -490,8 +490,8 @@ async def get_ollama_tags( key = get_api_key(url_idx, url, (await Config.get('ollama.api_configs', {}))) result = await send_request(f'{url}/api/tags', 'GET', key=key, user=user) - if user.role == 'user' and not BYPASS_MODEL_ACCESS_CONTROL: - result['models'] = await get_filtered_models(result, user) + if (user.role == 'user' or user.role == 'builder') and not BYPASS_MODEL_ACCESS_CONTROL: + models['models'] = await get_filtered_models(models, user) return result @@ -1493,7 +1493,7 @@ async def get_openai_models( now_ts = int(time.time()) models = [{'id': m['model'], 'object': 'model', 'created': now_ts, 'owned_by': 'openai'} for m in raw_models] - if user.role == 'user' and not BYPASS_MODEL_ACCESS_CONTROL: + if (user.role == 'user' or user.role == 'builder') and not BYPASS_MODEL_ACCESS_CONTROL: model_ids = [m['id'] for m in models] model_infos = {mi.id: mi for mi in await Models.get_models_by_ids(model_ids, db=db)} user_group_ids = {g.id for g in await Groups.get_groups_by_member_id(user.id, db=db)} diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index d5cad77bef4b..fa0d7387c3e6 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -779,7 +779,7 @@ async def get_models(request: Request, url_idx: int | None = None, user=Depends( error_detail = f'Unexpected error: {str(e)}' raise HTTPException(status_code=500, detail=error_detail) - if user.role == 'user' and not BYPASS_MODEL_ACCESS_CONTROL: + if (user.role == 'user' or user.role == 'builder') and not BYPASS_MODEL_ACCESS_CONTROL: models['data'] = await get_filtered_models(models, user) return models diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index fda632074998..6f6f3e2d05e3 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -199,7 +199,7 @@ async def generate_chat_completion( return await generate_direct_chat_completion(request, form_data, user=user, models=models) else: # Check if user has access to the model - if not bypass_filter and user.role == 'user': + if not bypass_filter and (user.role == 'user' or user.role == 'builder'): try: await check_model_access(user, model) except Exception as e: diff --git a/backend/open_webui/utils/embeddings.py b/backend/open_webui/utils/embeddings.py index a2049d4de616..2203b9506ee7 100644 --- a/backend/open_webui/utils/embeddings.py +++ b/backend/open_webui/utils/embeddings.py @@ -67,7 +67,7 @@ async def generate_embeddings( # Access filtering if not getattr(request.state, 'direct', False): - if not bypass_filter and user.role == 'user': + if not bypass_filter and (user.role == 'user' or user.role == 'builder'): await check_model_access(user, model) # Ollama backend — use /api/embed which supports batch input natively diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index b28216bd7f64..7eea5d934927 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -1524,6 +1524,7 @@ async def get_user_role(self, user, user_data): role = 'user' else: role = first_role + matched = True break for admin_role in oauth_admin_roles: if admin_role in oauth_roles: