From 2c7df302419bbca1a14ff84e023cda9743d5bf5b Mon Sep 17 00:00:00 2001 From: MiXaiLL76 Date: Fri, 23 Jan 2026 20:39:10 +0300 Subject: [PATCH] ldap.server.use_ad_sid --- backend/open_webui/config.py | 4 ++ backend/open_webui/main.py | 2 + backend/open_webui/models/auths.py | 3 +- backend/open_webui/routers/auths.py | 78 +++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) mode change 100644 => 100755 backend/open_webui/routers/auths.py diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 58810c9e3e..c8e2532b7f 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -4010,6 +4010,10 @@ LDAP_VALIDATE_CERT = PersistentConfig( LDAP_CIPHERS = PersistentConfig('LDAP_CIPHERS', 'ldap.server.ciphers', os.environ.get('LDAP_CIPHERS', 'ALL')) +LDAP_USE_AD_SID = PersistentConfig( + "LDAP_USE_AD_SID", "ldap.server.use_ad_sid", os.environ.get("LDAP_USE_AD_SID", "False").lower() == "true" +) + # For LDAP Group Management ENABLE_LDAP_GROUP_MANAGEMENT = PersistentConfig( 'ENABLE_LDAP_GROUP_MANAGEMENT', diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 0081788068..2c0aee0d0a 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -425,6 +425,7 @@ from open_webui.config import ( LDAP_CA_CERT_FILE, LDAP_VALIDATE_CERT, LDAP_CIPHERS, + LDAP_USE_AD_SID, # LDAP Group Management ENABLE_LDAP_GROUP_MANAGEMENT, ENABLE_LDAP_GROUP_CREATION, @@ -914,6 +915,7 @@ app.state.config.LDAP_USE_TLS = LDAP_USE_TLS app.state.config.LDAP_CA_CERT_FILE = LDAP_CA_CERT_FILE app.state.config.LDAP_VALIDATE_CERT = LDAP_VALIDATE_CERT app.state.config.LDAP_CIPHERS = LDAP_CIPHERS +app.state.config.LDAP_USE_AD_SID = LDAP_USE_AD_SID # For LDAP Group Management app.state.config.ENABLE_LDAP_GROUP_MANAGEMENT = ENABLE_LDAP_GROUP_MANAGEMENT diff --git a/backend/open_webui/models/auths.py b/backend/open_webui/models/auths.py index 1a1b164c12..7cda44a42b 100644 --- a/backend/open_webui/models/auths.py +++ b/backend/open_webui/models/auths.py @@ -97,12 +97,11 @@ class AuthsTable: role: str = 'pending', oauth: Optional[dict] = None, db: Optional[Session] = None, + id: Optional[str] = str(uuid.uuid4()), ) -> Optional[UserModel]: with get_db_context(db) as db: log.info('insert_new_auth') - id = str(uuid.uuid4()) - auth = AuthModel(**{'id': id, 'email': email, 'password': password, 'active': True}) result = Auth(**auth.model_dump()) db.add(result) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py old mode 100644 new mode 100755 index 88f0fe69fb..a05e26a181 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -302,6 +302,58 @@ async def update_password( raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) +############################ +# LDAP Helper Functions +############################ + + +def convert_ad_sid_to_string(sid_bytes): + """ + Convert Active Directory binary SID to string format. + + Args: + sid_bytes: Binary SID from AD (objectSid attribute) + + Returns: + String representation like "S-1-5-21-3396494274-2626632863-120886085-599475" + or None if conversion fails + """ + if not sid_bytes or not isinstance(sid_bytes, bytes): + return None + + try: + # SID structure: S-Revision-IdentifierAuthority-SubAuthority1-SubAuthority2-... + # Revision (1 byte) + revision = sid_bytes[0] + + # Number of SubAuthorities (1 byte) + sub_auth_count = sid_bytes[1] + + # IdentifierAuthority (6 bytes, big-endian) + identifier_authority = int.from_bytes(sid_bytes[2:8], byteorder='big') + + # SubAuthorities (4 bytes each, little-endian) + sub_authorities = [] + for i in range(sub_auth_count): + offset = 8 + (i * 4) + sub_auth = int.from_bytes( + sid_bytes[offset:offset + 4], + byteorder='little' + ) + sub_authorities.append(str(sub_auth)) + + # Construct SID string + sid_string = f"S-{revision}-{identifier_authority}" + if sub_authorities: + sid_string += "-" + "-".join(sub_authorities) + + return sid_string + + except Exception as e: + log.error(f"Failed to convert AD SID to string: {str(e)}") + return None + + ############################ # LDAP Authentication ############################ @@ -336,6 +388,7 @@ async def ldap_auth( LDAP_CA_CERT_FILE = request.app.state.config.LDAP_CA_CERT_FILE LDAP_VALIDATE_CERT = CERT_REQUIRED if request.app.state.config.LDAP_VALIDATE_CERT else CERT_NONE LDAP_CIPHERS = request.app.state.config.LDAP_CIPHERS if request.app.state.config.LDAP_CIPHERS else 'ALL' + LDAP_USE_AD_SID = request.app.state.config.LDAP_USE_AD_SID try: tls = Tls( @@ -378,6 +431,9 @@ async def ldap_auth( if ENABLE_LDAP_GROUP_MANAGEMENT: search_attributes.append(f'{LDAP_ATTRIBUTE_FOR_GROUPS}') log.info(f'LDAP Group Management enabled. Adding {LDAP_ATTRIBUTE_FOR_GROUPS} to search attributes') + if LDAP_USE_AD_SID: + search_attributes.append('objectSid') + log.info('LDAP AD SID usage enabled. Adding objectSid to search attributes') log.info(f'LDAP search attributes: {search_attributes}') search_success = await asyncio.to_thread( @@ -412,6 +468,22 @@ async def ldap_auth( cn = str(entry['cn']) # common name user_dn = entry.entry_dn # user distinguished name + # Extract and convert AD SID if enabled + user_id = None + if LDAP_USE_AD_SID and "objectSid" in entry: + try: + sid_bytes = entry["objectSid"].value + if sid_bytes: + user_id = convert_ad_sid_to_string(sid_bytes) + if user_id: + log.info(f"Successfully extracted AD SID for user {username_list}: {user_id}") + else: + log.warning(f"Failed to convert AD SID for user {username_list}, will use UUID fallback") + except Exception as e: + log.warning(f"Error extracting AD SID for user {username_list}: {str(e)}, will use UUID fallback") + elif LDAP_USE_AD_SID: + log.warning(f"LDAP_USE_AD_SID enabled but objectSid not found for user {username_list}, will use UUID fallback") + user_groups = [] if ENABLE_LDAP_GROUP_MANAGEMENT and LDAP_ATTRIBUTE_FOR_GROUPS in entry: group_dns = entry[LDAP_ATTRIBUTE_FOR_GROUPS] @@ -481,12 +553,18 @@ async def ldap_auth( try: role = 'admin' if not Users.has_users(db=db) else request.app.state.config.DEFAULT_USER_ROLE + kwargs = {} + if user_id: + log.info(f"Creating LDAP user with AD SID as ID: {user_id}") + kwargs["id"] = user_id + user = Auths.insert_new_auth( email=email, password=str(uuid.uuid4()), name=cn, role=role, db=db, + **kwargs, ) if not user: