refac
This commit is contained in:
@@ -1,18 +1,17 @@
|
||||
import base64
|
||||
import copy
|
||||
import fnmatch
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
import re
|
||||
import secrets
|
||||
import sys
|
||||
import time
|
||||
import urllib
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timedelta
|
||||
from types import SimpleNamespace
|
||||
from typing import Literal, Optional
|
||||
|
||||
import aiohttp
|
||||
@@ -62,7 +61,6 @@ from open_webui.config import (
|
||||
OAUTH_UPDATE_PICTURE_ON_LOGIN,
|
||||
OAUTH_USERNAME_CLAIM,
|
||||
WEBHOOK_URL,
|
||||
AppConfig,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
||||
from open_webui.env import (
|
||||
@@ -78,6 +76,7 @@ from open_webui.env import (
|
||||
WEBUI_NAME,
|
||||
)
|
||||
from open_webui.models.auths import Auths
|
||||
from open_webui.models.config import Config
|
||||
from open_webui.models.groups import GroupForm, GroupModel, Groups, GroupUpdateForm
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
from open_webui.models.users import Users
|
||||
@@ -111,31 +110,73 @@ from open_webui.env import GLOBAL_LOG_LEVEL
|
||||
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
auth_manager_config = AppConfig()
|
||||
auth_manager_config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
|
||||
auth_manager_config.ENABLE_OAUTH_SIGNUP = ENABLE_OAUTH_SIGNUP
|
||||
auth_manager_config.OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE = OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE
|
||||
auth_manager_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL = OAUTH_MERGE_ACCOUNTS_BY_EMAIL
|
||||
auth_manager_config.ENABLE_OAUTH_ROLE_MANAGEMENT = ENABLE_OAUTH_ROLE_MANAGEMENT
|
||||
auth_manager_config.ENABLE_OAUTH_GROUP_MANAGEMENT = ENABLE_OAUTH_GROUP_MANAGEMENT
|
||||
auth_manager_config.ENABLE_OAUTH_GROUP_CREATION = ENABLE_OAUTH_GROUP_CREATION
|
||||
auth_manager_config.OAUTH_GROUP_DEFAULT_SHARE = OAUTH_GROUP_DEFAULT_SHARE
|
||||
auth_manager_config.OAUTH_BLOCKED_GROUPS = OAUTH_BLOCKED_GROUPS
|
||||
auth_manager_config.OAUTH_ROLES_CLAIM = OAUTH_ROLES_CLAIM
|
||||
auth_manager_config.OAUTH_SUB_CLAIM = OAUTH_SUB_CLAIM
|
||||
auth_manager_config.OAUTH_GROUPS_CLAIM = OAUTH_GROUPS_CLAIM
|
||||
auth_manager_config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
|
||||
auth_manager_config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
|
||||
auth_manager_config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
|
||||
auth_manager_config.OAUTH_ALLOWED_ROLES = OAUTH_ALLOWED_ROLES
|
||||
auth_manager_config.OAUTH_ADMIN_ROLES = OAUTH_ADMIN_ROLES
|
||||
auth_manager_config.OAUTH_ALLOWED_DOMAINS = OAUTH_ALLOWED_DOMAINS
|
||||
auth_manager_config.WEBHOOK_URL = WEBHOOK_URL
|
||||
auth_manager_config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
|
||||
auth_manager_config.OAUTH_UPDATE_PICTURE_ON_LOGIN = OAUTH_UPDATE_PICTURE_ON_LOGIN
|
||||
auth_manager_config.OAUTH_UPDATE_NAME_ON_LOGIN = OAUTH_UPDATE_NAME_ON_LOGIN
|
||||
auth_manager_config.OAUTH_UPDATE_EMAIL_ON_LOGIN = OAUTH_UPDATE_EMAIL_ON_LOGIN
|
||||
auth_manager_config.OAUTH_AUDIENCE = OAUTH_AUDIENCE
|
||||
OAUTH_RUNTIME_CONFIG = {
|
||||
'DEFAULT_USER_ROLE': ('ui.default_user_role', DEFAULT_USER_ROLE),
|
||||
'ENABLE_OAUTH_SIGNUP': ('oauth.enable_signup', ENABLE_OAUTH_SIGNUP),
|
||||
'OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE': (
|
||||
'oauth.refresh_token.include_scope',
|
||||
OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE,
|
||||
),
|
||||
'OAUTH_MERGE_ACCOUNTS_BY_EMAIL': (
|
||||
'oauth.merge_accounts_by_email',
|
||||
OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
|
||||
),
|
||||
'ENABLE_OAUTH_ROLE_MANAGEMENT': (
|
||||
'oauth.enable_role_mapping',
|
||||
ENABLE_OAUTH_ROLE_MANAGEMENT,
|
||||
),
|
||||
'ENABLE_OAUTH_GROUP_MANAGEMENT': (
|
||||
'oauth.enable_group_mapping',
|
||||
ENABLE_OAUTH_GROUP_MANAGEMENT,
|
||||
),
|
||||
'ENABLE_OAUTH_GROUP_CREATION': (
|
||||
'oauth.enable_group_creation',
|
||||
ENABLE_OAUTH_GROUP_CREATION,
|
||||
),
|
||||
'OAUTH_GROUP_DEFAULT_SHARE': (
|
||||
'oauth.group_default_share',
|
||||
OAUTH_GROUP_DEFAULT_SHARE,
|
||||
),
|
||||
'OAUTH_BLOCKED_GROUPS': ('oauth.blocked_groups', OAUTH_BLOCKED_GROUPS),
|
||||
'OAUTH_ROLES_CLAIM': ('oauth.roles_claim', OAUTH_ROLES_CLAIM),
|
||||
'OAUTH_SUB_CLAIM': ('oauth.sub_claim', OAUTH_SUB_CLAIM),
|
||||
'OAUTH_GROUPS_CLAIM': ('oauth.group_claim', OAUTH_GROUPS_CLAIM),
|
||||
'OAUTH_EMAIL_CLAIM': ('oauth.email_claim', OAUTH_EMAIL_CLAIM),
|
||||
'OAUTH_PICTURE_CLAIM': ('oauth.picture_claim', OAUTH_PICTURE_CLAIM),
|
||||
'OAUTH_USERNAME_CLAIM': ('oauth.username_claim', OAUTH_USERNAME_CLAIM),
|
||||
'OAUTH_ALLOWED_ROLES': ('oauth.allowed_roles', OAUTH_ALLOWED_ROLES),
|
||||
'OAUTH_ADMIN_ROLES': ('oauth.admin_roles', OAUTH_ADMIN_ROLES),
|
||||
'OAUTH_ALLOWED_DOMAINS': ('oauth.allowed_domains', OAUTH_ALLOWED_DOMAINS),
|
||||
'WEBHOOK_URL': ('webhook_url', WEBHOOK_URL),
|
||||
'JWT_EXPIRES_IN': ('auth.jwt_expiry', JWT_EXPIRES_IN),
|
||||
'OAUTH_UPDATE_PICTURE_ON_LOGIN': (
|
||||
'oauth.update_picture_on_login',
|
||||
OAUTH_UPDATE_PICTURE_ON_LOGIN,
|
||||
),
|
||||
'OAUTH_UPDATE_NAME_ON_LOGIN': (
|
||||
'oauth.update_name_on_login',
|
||||
OAUTH_UPDATE_NAME_ON_LOGIN,
|
||||
),
|
||||
'OAUTH_UPDATE_EMAIL_ON_LOGIN': (
|
||||
'oauth.update_email_on_login',
|
||||
OAUTH_UPDATE_EMAIL_ON_LOGIN,
|
||||
),
|
||||
'OAUTH_AUDIENCE': ('oauth.audience', OAUTH_AUDIENCE),
|
||||
}
|
||||
|
||||
|
||||
def _default_value(value):
|
||||
return getattr(value, 'value', value)
|
||||
|
||||
|
||||
async def get_oauth_runtime_config() -> SimpleNamespace:
|
||||
keys = [key for key, _default in OAUTH_RUNTIME_CONFIG.values()]
|
||||
stored = await Config.get_many(*keys)
|
||||
values = {
|
||||
name: stored.get(key, _default_value(default))
|
||||
for name, (key, default) in OAUTH_RUNTIME_CONFIG.items()
|
||||
}
|
||||
return SimpleNamespace(**values)
|
||||
|
||||
|
||||
# Conservative default when the provider omits both expires_in and expires_at.
|
||||
@@ -423,7 +464,8 @@ async def get_oauth_client_info_with_dynamic_client_registration(
|
||||
oauth_server_metadata = None
|
||||
oauth_server_metadata_url = None
|
||||
|
||||
redirect_base_url = (str(request.app.state.config.WEBUI_URL or request.base_url)).rstrip('/')
|
||||
webui_url = await Config.get('webui.url')
|
||||
redirect_base_url = (str(webui_url or request.base_url)).rstrip('/')
|
||||
|
||||
oauth_client_metadata = OAuthClientMetadata(
|
||||
client_name='Open WebUI',
|
||||
@@ -549,7 +591,8 @@ async def get_oauth_client_info_with_static_credentials(
|
||||
oauth_server_metadata = None
|
||||
oauth_server_metadata_url = None
|
||||
|
||||
redirect_base_url = (str(request.app.state.config.WEBUI_URL or request.base_url)).rstrip('/')
|
||||
webui_url = await Config.get('webui.url')
|
||||
redirect_base_url = (str(webui_url or request.base_url)).rstrip('/')
|
||||
redirect_uri = f'{redirect_base_url}/oauth/clients/{client_id}/callback'
|
||||
|
||||
# Discover server metadata (authorization endpoint, token endpoint, scopes, etc.)
|
||||
@@ -636,7 +679,7 @@ class OAuthClientManager:
|
||||
'client_secret': oauth_client_info.client_secret,
|
||||
'client_kwargs': {
|
||||
'follow_redirects': True,
|
||||
**({'timeout': int(OAUTH_CLIENT_TIMEOUT.value)} if OAUTH_CLIENT_TIMEOUT.value else {}),
|
||||
**({'timeout': int(OAUTH_CLIENT_TIMEOUT)} if OAUTH_CLIENT_TIMEOUT else {}),
|
||||
**({'scope': oauth_client_info.scope} if oauth_client_info.scope else {}),
|
||||
**(
|
||||
{'token_endpoint_auth_method': oauth_client_info.token_endpoint_auth_method}
|
||||
@@ -668,7 +711,7 @@ class OAuthClientManager:
|
||||
}
|
||||
return self.clients[client_id]
|
||||
|
||||
def ensure_client_from_config(self, client_id):
|
||||
async def ensure_client_from_config(self, client_id):
|
||||
"""
|
||||
Lazy-load an OAuth client from the current TOOL_SERVER_CONNECTIONS
|
||||
config if it hasn't been registered on this node yet.
|
||||
@@ -677,7 +720,7 @@ class OAuthClientManager:
|
||||
return self.clients[client_id]['client']
|
||||
|
||||
try:
|
||||
connections = getattr(self.app.state.config, 'TOOL_SERVER_CONNECTIONS', [])
|
||||
connections = await Config.get('tool_server.connections', [])
|
||||
except Exception:
|
||||
connections = []
|
||||
|
||||
@@ -783,22 +826,22 @@ class OAuthClientManager:
|
||||
|
||||
return True
|
||||
|
||||
def get_client(self, client_id):
|
||||
async def get_client(self, client_id):
|
||||
if client_id not in self.clients:
|
||||
self.ensure_client_from_config(client_id)
|
||||
await self.ensure_client_from_config(client_id)
|
||||
|
||||
client = self.clients.get(client_id)
|
||||
return client['client'] if client else None
|
||||
|
||||
def get_client_info(self, client_id):
|
||||
async def get_client_info(self, client_id):
|
||||
if client_id not in self.clients:
|
||||
self.ensure_client_from_config(client_id)
|
||||
await self.ensure_client_from_config(client_id)
|
||||
|
||||
client = self.clients.get(client_id)
|
||||
return client['client_info'] if client else None
|
||||
|
||||
def get_server_metadata_url(self, client_id):
|
||||
client = self.get_client(client_id)
|
||||
async def get_server_metadata_url(self, client_id):
|
||||
client = await self.get_client(client_id)
|
||||
if not client:
|
||||
return None
|
||||
|
||||
@@ -881,6 +924,7 @@ class OAuthClientManager:
|
||||
Returns:
|
||||
dict: New token data, or None if refresh failed
|
||||
"""
|
||||
auth_config = await get_oauth_runtime_config()
|
||||
client_id = session.provider
|
||||
token_data = session.token
|
||||
|
||||
@@ -889,14 +933,14 @@ class OAuthClientManager:
|
||||
return None
|
||||
|
||||
try:
|
||||
client = self.get_client(client_id)
|
||||
client = await self.get_client(client_id)
|
||||
if not client:
|
||||
log.error(f'No OAuth client found for provider {client_id}')
|
||||
return None
|
||||
|
||||
token_endpoint = None
|
||||
async with aiohttp.ClientSession(trust_env=True) as session_http:
|
||||
async with session_http.get(self.get_server_metadata_url(client_id)) as r:
|
||||
async with session_http.get(await self.get_server_metadata_url(client_id)) as r:
|
||||
if r.status == 200:
|
||||
openid_data = await r.json()
|
||||
token_endpoint = openid_data.get('token_endpoint')
|
||||
@@ -913,7 +957,7 @@ class OAuthClientManager:
|
||||
'client_id': client.client_id,
|
||||
}
|
||||
# RFC 8707: include resource indicator so refreshed tokens retain correct audience
|
||||
client_info = self.get_client_info(client_id)
|
||||
client_info = await self.get_client_info(client_id)
|
||||
if client_info and client_info.resource:
|
||||
refresh_data['resource'] = client_info.resource
|
||||
|
||||
@@ -924,7 +968,7 @@ class OAuthClientManager:
|
||||
if (
|
||||
hasattr(client, 'client_kwargs')
|
||||
and client.client_kwargs.get('scope')
|
||||
and getattr(self.app.state.config, 'OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE', False)
|
||||
and auth_config.OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE
|
||||
):
|
||||
refresh_data['scope'] = client.client_kwargs['scope']
|
||||
|
||||
@@ -957,13 +1001,13 @@ class OAuthClientManager:
|
||||
return None
|
||||
|
||||
async def handle_authorize(self, request, client_id: str) -> RedirectResponse:
|
||||
client = self.get_client(client_id) or self.ensure_client_from_config(client_id)
|
||||
client = await self.get_client(client_id)
|
||||
if client is None:
|
||||
raise HTTPException(404)
|
||||
client_info = self.get_client_info(client_id)
|
||||
client_info = await self.get_client_info(client_id)
|
||||
if client_info is None:
|
||||
# ensure_client_from_config registers client_info too
|
||||
client_info = self.get_client_info(client_id)
|
||||
# get_client registers client_info too
|
||||
client_info = await self.get_client_info(client_id)
|
||||
if client_info is None:
|
||||
raise HTTPException(404)
|
||||
|
||||
@@ -976,13 +1020,13 @@ class OAuthClientManager:
|
||||
return await client.authorize_redirect(request, redirect_uri_str, **kwargs)
|
||||
|
||||
async def handle_callback(self, request, client_id: str, user_id: str, response):
|
||||
client = self.get_client(client_id) or self.ensure_client_from_config(client_id)
|
||||
client = await self.get_client(client_id)
|
||||
if client is None:
|
||||
raise HTTPException(404)
|
||||
|
||||
error_message = None
|
||||
try:
|
||||
client_info = self.get_client_info(client_id)
|
||||
client_info = await self.get_client_info(client_id)
|
||||
|
||||
# Note: Do NOT pass client_id/client_secret explicitly here.
|
||||
# The Authlib client already has these configured during add_client().
|
||||
@@ -1035,7 +1079,8 @@ class OAuthClientManager:
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
redirect_url = (str(request.app.state.config.WEBUI_URL or request.base_url)).rstrip('/')
|
||||
webui_url = await Config.get('webui.url')
|
||||
redirect_url = (str(webui_url or request.base_url)).rstrip('/')
|
||||
|
||||
if error_message:
|
||||
log.debug(error_message)
|
||||
@@ -1202,7 +1247,7 @@ class OAuthManager:
|
||||
if (
|
||||
hasattr(client, 'client_kwargs')
|
||||
and client.client_kwargs.get('scope')
|
||||
and auth_manager_config.OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE
|
||||
and auth_config.OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE
|
||||
):
|
||||
refresh_data['scope'] = client.client_kwargs['scope']
|
||||
|
||||
@@ -1235,6 +1280,7 @@ class OAuthManager:
|
||||
return None
|
||||
|
||||
async def get_user_role(self, user, user_data):
|
||||
auth_config = await get_oauth_runtime_config()
|
||||
user_count = await Users.get_num_users()
|
||||
if user and user_count == 1:
|
||||
# If the user is the only user, assign the role "admin" - actually repairs role for single user on login
|
||||
@@ -1246,16 +1292,16 @@ class OAuthManager:
|
||||
# default role here (not 'admin') — admin promotion happens
|
||||
# race-safely *after* insert via get_num_users() == 1.
|
||||
log.debug('First user bootstrap: using default role (admin promotion deferred to post-insert)')
|
||||
return auth_manager_config.DEFAULT_USER_ROLE
|
||||
return auth_config.DEFAULT_USER_ROLE
|
||||
|
||||
if auth_manager_config.ENABLE_OAUTH_ROLE_MANAGEMENT:
|
||||
if auth_config.ENABLE_OAUTH_ROLE_MANAGEMENT:
|
||||
log.debug('Running OAUTH Role management')
|
||||
oauth_claim = auth_manager_config.OAUTH_ROLES_CLAIM
|
||||
oauth_allowed_roles = auth_manager_config.OAUTH_ALLOWED_ROLES
|
||||
oauth_admin_roles = auth_manager_config.OAUTH_ADMIN_ROLES
|
||||
oauth_claim = auth_config.OAUTH_ROLES_CLAIM
|
||||
oauth_allowed_roles = auth_config.OAUTH_ALLOWED_ROLES
|
||||
oauth_admin_roles = auth_config.OAUTH_ADMIN_ROLES
|
||||
oauth_roles = []
|
||||
# Default/fallback role if no matching roles are found
|
||||
role = auth_manager_config.DEFAULT_USER_ROLE
|
||||
role = auth_config.DEFAULT_USER_ROLE
|
||||
|
||||
# Next block extracts the roles from the user data, accepting nested claims of any depth
|
||||
if oauth_claim and oauth_allowed_roles and oauth_admin_roles:
|
||||
@@ -1313,7 +1359,7 @@ class OAuthManager:
|
||||
else:
|
||||
if not user:
|
||||
# If role management is disabled, use the default role for new users
|
||||
role = auth_manager_config.DEFAULT_USER_ROLE
|
||||
role = auth_config.DEFAULT_USER_ROLE
|
||||
else:
|
||||
# If role management is disabled, use the existing role for existing users
|
||||
role = user.role
|
||||
@@ -1321,11 +1367,12 @@ class OAuthManager:
|
||||
return role
|
||||
|
||||
async def update_user_groups(self, user, user_data, default_permissions, db=None):
|
||||
auth_config = await get_oauth_runtime_config()
|
||||
log.debug('Running OAUTH Group management')
|
||||
oauth_claim = auth_manager_config.OAUTH_GROUPS_CLAIM
|
||||
oauth_claim = auth_config.OAUTH_GROUPS_CLAIM
|
||||
|
||||
try:
|
||||
blocked_groups = json.loads(auth_manager_config.OAUTH_BLOCKED_GROUPS)
|
||||
blocked_groups = json.loads(auth_config.OAUTH_BLOCKED_GROUPS)
|
||||
except Exception as e:
|
||||
log.exception(f'Error loading OAUTH_BLOCKED_GROUPS: {e}')
|
||||
blocked_groups = []
|
||||
@@ -1353,7 +1400,7 @@ class OAuthManager:
|
||||
all_available_groups: list[GroupModel] = await Groups.get_all_groups(db=db)
|
||||
|
||||
# Create groups if they don't exist and creation is enabled
|
||||
if auth_manager_config.ENABLE_OAUTH_GROUP_CREATION:
|
||||
if auth_config.ENABLE_OAUTH_GROUP_CREATION:
|
||||
log.debug('Checking for missing groups to create...')
|
||||
all_group_names = {g.name for g in all_available_groups}
|
||||
groups_created = False
|
||||
@@ -1370,7 +1417,7 @@ class OAuthManager:
|
||||
name=group_name,
|
||||
description=f"Group '{group_name}' created automatically via OAuth.",
|
||||
permissions=default_permissions, # Use default permissions from function args
|
||||
data={'config': {'share': auth_manager_config.OAUTH_GROUP_DEFAULT_SHARE}},
|
||||
data={'config': {'share': auth_config.OAUTH_GROUP_DEFAULT_SHARE}},
|
||||
)
|
||||
# Use determined creator ID (admin or fallback to current user)
|
||||
created_group = await Groups.insert_new_group(creator_id, new_group_form, db=db)
|
||||
@@ -1496,6 +1543,7 @@ class OAuthManager:
|
||||
return '/user.png'
|
||||
|
||||
async def handle_login(self, request, provider):
|
||||
auth_config = await get_oauth_runtime_config()
|
||||
if provider not in OAUTH_PROVIDERS:
|
||||
raise HTTPException(404)
|
||||
# If the provider has a custom redirect URL, use that, otherwise automatically generate one
|
||||
@@ -1507,14 +1555,15 @@ class OAuthManager:
|
||||
)
|
||||
|
||||
kwargs = {}
|
||||
if auth_manager_config.OAUTH_AUDIENCE:
|
||||
kwargs['audience'] = auth_manager_config.OAUTH_AUDIENCE
|
||||
if auth_config.OAUTH_AUDIENCE:
|
||||
kwargs['audience'] = auth_config.OAUTH_AUDIENCE
|
||||
if OAUTH_AUTHORIZE_PARAMS:
|
||||
kwargs.update(OAUTH_AUTHORIZE_PARAMS)
|
||||
|
||||
return await client.authorize_redirect(request, redirect_uri, **kwargs)
|
||||
|
||||
async def handle_callback(self, request, provider, response, db=None):
|
||||
auth_config = await get_oauth_runtime_config()
|
||||
if provider not in OAUTH_PROVIDERS:
|
||||
raise HTTPException(404)
|
||||
|
||||
@@ -1568,8 +1617,8 @@ class OAuthManager:
|
||||
id_token_claims = dict(user_data) if user_data else {}
|
||||
if (
|
||||
(not user_data)
|
||||
or (auth_manager_config.OAUTH_EMAIL_CLAIM not in user_data)
|
||||
or (auth_manager_config.OAUTH_USERNAME_CLAIM not in user_data)
|
||||
or (auth_config.OAUTH_EMAIL_CLAIM not in user_data)
|
||||
or (auth_config.OAUTH_USERNAME_CLAIM not in user_data)
|
||||
):
|
||||
user_data: UserInfo = await client.userinfo(token=token)
|
||||
# Merge back ID token claims that the userinfo endpoint doesn't
|
||||
@@ -1585,8 +1634,8 @@ class OAuthManager:
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
|
||||
# Extract the "sub" claim, using custom claim if configured
|
||||
if auth_manager_config.OAUTH_SUB_CLAIM:
|
||||
sub = user_data.get(auth_manager_config.OAUTH_SUB_CLAIM)
|
||||
if auth_config.OAUTH_SUB_CLAIM:
|
||||
sub = user_data.get(auth_config.OAUTH_SUB_CLAIM)
|
||||
else:
|
||||
# Fallback to the default sub claim if not configured
|
||||
sub = user_data.get(OAUTH_PROVIDERS[provider].get('sub_claim', 'sub'))
|
||||
@@ -1600,7 +1649,7 @@ class OAuthManager:
|
||||
}
|
||||
|
||||
# Email extraction
|
||||
email_claim = auth_manager_config.OAUTH_EMAIL_CLAIM
|
||||
email_claim = auth_config.OAUTH_EMAIL_CLAIM
|
||||
email = user_data.get(email_claim, '')
|
||||
# We currently mandate that email addresses are provided
|
||||
if not email:
|
||||
@@ -1642,8 +1691,8 @@ class OAuthManager:
|
||||
email = email.lower()
|
||||
# If allowed domains are configured, check if the email domain is in the list
|
||||
if (
|
||||
'*' not in auth_manager_config.OAUTH_ALLOWED_DOMAINS
|
||||
and email.split('@')[-1] not in auth_manager_config.OAUTH_ALLOWED_DOMAINS
|
||||
'*' not in auth_config.OAUTH_ALLOWED_DOMAINS
|
||||
and email.split('@')[-1] not in auth_config.OAUTH_ALLOWED_DOMAINS
|
||||
):
|
||||
log.warning(f'OAuth callback failed, e-mail domain is not in the list of allowed domains: {user_data}')
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
@@ -1652,7 +1701,7 @@ class OAuthManager:
|
||||
user = await Users.get_user_by_oauth_sub(provider, sub, db=db)
|
||||
if not user:
|
||||
# If the user does not exist, check if merging is enabled
|
||||
if auth_manager_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL:
|
||||
if auth_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL:
|
||||
# Check if the user exists by email
|
||||
user = await Users.get_user_by_email(email, db=db)
|
||||
if user:
|
||||
@@ -1667,8 +1716,8 @@ class OAuthManager:
|
||||
# to avoid problems with the ENABLE_OAUTH_GROUP_MANAGEMENT check below
|
||||
user.role = determined_role
|
||||
|
||||
if auth_manager_config.OAUTH_UPDATE_NAME_ON_LOGIN:
|
||||
username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM
|
||||
if auth_config.OAUTH_UPDATE_NAME_ON_LOGIN:
|
||||
username_claim = auth_config.OAUTH_USERNAME_CLAIM
|
||||
if username_claim:
|
||||
new_name = user_data.get(username_claim)
|
||||
if new_name and new_name != user.name:
|
||||
@@ -1676,8 +1725,8 @@ class OAuthManager:
|
||||
user.name = new_name
|
||||
log.debug(f'Updated name for user {user.email}')
|
||||
|
||||
if auth_manager_config.OAUTH_UPDATE_EMAIL_ON_LOGIN:
|
||||
email_claim = auth_manager_config.OAUTH_EMAIL_CLAIM
|
||||
if auth_config.OAUTH_UPDATE_EMAIL_ON_LOGIN:
|
||||
email_claim = auth_config.OAUTH_EMAIL_CLAIM
|
||||
if email_claim:
|
||||
new_email = user_data.get(email_claim)
|
||||
if new_email and new_email.lower() != user.email.lower():
|
||||
@@ -1692,8 +1741,8 @@ class OAuthManager:
|
||||
log.debug(f'Updated email for user {user.id}')
|
||||
|
||||
# Update profile picture if enabled and different from current
|
||||
if auth_manager_config.OAUTH_UPDATE_PICTURE_ON_LOGIN:
|
||||
picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM
|
||||
if auth_config.OAUTH_UPDATE_PICTURE_ON_LOGIN:
|
||||
picture_claim = auth_config.OAUTH_PICTURE_CLAIM
|
||||
if picture_claim:
|
||||
new_picture_url = user_data.get(
|
||||
picture_claim,
|
||||
@@ -1707,13 +1756,13 @@ class OAuthManager:
|
||||
log.debug(f'Updated profile picture for user {user.email}')
|
||||
else:
|
||||
# If the user does not exist, check if signups are enabled
|
||||
if auth_manager_config.ENABLE_OAUTH_SIGNUP:
|
||||
if auth_config.ENABLE_OAUTH_SIGNUP:
|
||||
# Check if an existing user with the same email already exists
|
||||
existing_user = await Users.get_user_by_email(email, db=db)
|
||||
if existing_user:
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
||||
|
||||
picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM
|
||||
picture_claim = auth_config.OAUTH_PICTURE_CLAIM
|
||||
if picture_claim:
|
||||
picture_url = user_data.get(
|
||||
picture_claim,
|
||||
@@ -1722,7 +1771,7 @@ class OAuthManager:
|
||||
picture_url = await self._process_picture_url(picture_url, token.get('access_token'))
|
||||
else:
|
||||
picture_url = '/user.png'
|
||||
username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM
|
||||
username_claim = auth_config.OAUTH_USERNAME_CLAIM
|
||||
|
||||
name = user_data.get(username_claim)
|
||||
if not name:
|
||||
@@ -1749,10 +1798,10 @@ class OAuthManager:
|
||||
await Users.update_user_role_by_id(user.id, 'admin', db=db)
|
||||
user = await Users.get_user_by_id(user.id, db=db)
|
||||
|
||||
if auth_manager_config.WEBHOOK_URL:
|
||||
if auth_config.WEBHOOK_URL:
|
||||
await post_webhook(
|
||||
WEBUI_NAME,
|
||||
auth_manager_config.WEBHOOK_URL,
|
||||
auth_config.WEBHOOK_URL,
|
||||
WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
|
||||
{
|
||||
'action': 'signup',
|
||||
@@ -1761,7 +1810,8 @@ class OAuthManager:
|
||||
},
|
||||
)
|
||||
|
||||
await apply_default_group_assignment(request.app.state.config.DEFAULT_GROUP_ID, user.id, db=db)
|
||||
default_group_id = await Config.get('ui.default_group_id')
|
||||
await apply_default_group_assignment(default_group_id, user.id, db=db)
|
||||
|
||||
else:
|
||||
raise HTTPException(
|
||||
@@ -1771,13 +1821,13 @@ class OAuthManager:
|
||||
|
||||
jwt_token = create_token(
|
||||
data={'id': user.id},
|
||||
expires_delta=parse_duration(auth_manager_config.JWT_EXPIRES_IN),
|
||||
expires_delta=parse_duration(auth_config.JWT_EXPIRES_IN),
|
||||
)
|
||||
if auth_manager_config.ENABLE_OAUTH_GROUP_MANAGEMENT:
|
||||
if auth_config.ENABLE_OAUTH_GROUP_MANAGEMENT:
|
||||
await self.update_user_groups(
|
||||
user=user,
|
||||
user_data=user_data,
|
||||
default_permissions=request.app.state.config.USER_PERMISSIONS,
|
||||
default_permissions=await Config.get('user.permissions'),
|
||||
db=db,
|
||||
)
|
||||
|
||||
@@ -1789,7 +1839,8 @@ class OAuthManager:
|
||||
else ERROR_MESSAGES.DEFAULT('Error during OAuth process')
|
||||
)
|
||||
|
||||
redirect_base_url = (str(request.app.state.config.WEBUI_URL or request.base_url)).rstrip('/')
|
||||
webui_url = await Config.get('webui.url')
|
||||
redirect_base_url = (str(webui_url or request.base_url)).rstrip('/')
|
||||
redirect_url = f'{redirect_base_url}/auth'
|
||||
|
||||
if error_message:
|
||||
@@ -1799,7 +1850,7 @@ class OAuthManager:
|
||||
response = RedirectResponse(url=redirect_url, headers=response.headers)
|
||||
|
||||
# Compute cookie expiry from JWT lifetime
|
||||
expires_delta = parse_duration(auth_manager_config.JWT_EXPIRES_IN)
|
||||
expires_delta = parse_duration(auth_config.JWT_EXPIRES_IN)
|
||||
cookie_max_age = int(expires_delta.total_seconds()) if expires_delta else None
|
||||
|
||||
# Set the cookie token
|
||||
|
||||
Reference in New Issue
Block a user