ldap.server.use_ad_sid

This commit is contained in:
MiXaiLL76 2026-01-23 20:39:10 +03:00
parent 1c5e84ddf2
commit 2c7df30241
4 changed files with 85 additions and 2 deletions

View file

@ -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',

View file

@ -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

View file

@ -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)

78
backend/open_webui/routers/auths.py Normal file → Executable file
View file

@ -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: