mirror of
https://github.com/open-webui/open-webui
synced 2025-06-16 19:31:52 +00:00
Configure LDAP group synchronization with Open WebUI
This commit is contained in:
parent
9123c57c8a
commit
ba591d8c41
@ -3075,3 +3075,23 @@ LDAP_VALIDATE_CERT = PersistentConfig(
|
|||||||
LDAP_CIPHERS = PersistentConfig(
|
LDAP_CIPHERS = PersistentConfig(
|
||||||
"LDAP_CIPHERS", "ldap.server.ciphers", os.environ.get("LDAP_CIPHERS", "ALL")
|
"LDAP_CIPHERS", "ldap.server.ciphers", os.environ.get("LDAP_CIPHERS", "ALL")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# For LDAP Group Management
|
||||||
|
ENABLE_LDAP_GROUP_MANAGEMENT = PersistentConfig(
|
||||||
|
"ENABLE_LDAP_GROUP_MANAGEMENT",
|
||||||
|
"ldap.group.enable_management",
|
||||||
|
os.environ.get("ENABLE_LDAP_GROUP_MANAGEMENT", "False").lower() == "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
ENABLE_LDAP_GROUP_CREATION = PersistentConfig(
|
||||||
|
"ENABLE_LDAP_GROUP_CREATION",
|
||||||
|
"ldap.group.enable_creation",
|
||||||
|
os.environ.get("ENABLE_LDAP_GROUP_CREATION", "False").lower() == "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
LDAP_ATTRIBUTE_FOR_GROUPS = PersistentConfig(
|
||||||
|
"LDAP_ATTRIBUTE_FOR_GROUPS",
|
||||||
|
"ldap.server.attribute_for_groups",
|
||||||
|
os.environ.get("LDAP_ATTRIBUTE_FOR_GROUPS", "memberOf"),
|
||||||
|
)
|
||||||
|
|
||||||
|
@ -349,6 +349,10 @@ from open_webui.config import (
|
|||||||
LDAP_CA_CERT_FILE,
|
LDAP_CA_CERT_FILE,
|
||||||
LDAP_VALIDATE_CERT,
|
LDAP_VALIDATE_CERT,
|
||||||
LDAP_CIPHERS,
|
LDAP_CIPHERS,
|
||||||
|
# LDAP Group Management
|
||||||
|
ENABLE_LDAP_GROUP_MANAGEMENT,
|
||||||
|
ENABLE_LDAP_GROUP_CREATION,
|
||||||
|
LDAP_ATTRIBUTE_FOR_GROUPS,
|
||||||
# Misc
|
# Misc
|
||||||
ENV,
|
ENV,
|
||||||
CACHE_DIR,
|
CACHE_DIR,
|
||||||
@ -676,6 +680,11 @@ app.state.config.LDAP_CA_CERT_FILE = LDAP_CA_CERT_FILE
|
|||||||
app.state.config.LDAP_VALIDATE_CERT = LDAP_VALIDATE_CERT
|
app.state.config.LDAP_VALIDATE_CERT = LDAP_VALIDATE_CERT
|
||||||
app.state.config.LDAP_CIPHERS = LDAP_CIPHERS
|
app.state.config.LDAP_CIPHERS = LDAP_CIPHERS
|
||||||
|
|
||||||
|
# For LDAP Group Management
|
||||||
|
app.state.config.ENABLE_LDAP_GROUP_MANAGEMENT = ENABLE_LDAP_GROUP_MANAGEMENT
|
||||||
|
app.state.config.ENABLE_LDAP_GROUP_CREATION = ENABLE_LDAP_GROUP_CREATION
|
||||||
|
app.state.config.LDAP_ATTRIBUTE_FOR_GROUPS = LDAP_ATTRIBUTE_FOR_GROUPS
|
||||||
|
|
||||||
|
|
||||||
app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
|
app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
|
||||||
app.state.AUTH_TRUSTED_NAME_HEADER = WEBUI_AUTH_TRUSTED_NAME_HEADER
|
app.state.AUTH_TRUSTED_NAME_HEADER = WEBUI_AUTH_TRUSTED_NAME_HEADER
|
||||||
|
@ -228,14 +228,25 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
|
|||||||
if not connection_app.bind():
|
if not connection_app.bind():
|
||||||
raise HTTPException(400, detail="Application account bind failed")
|
raise HTTPException(400, detail="Application account bind failed")
|
||||||
|
|
||||||
search_success = connection_app.search(
|
ENABLE_LDAP_GROUP_MANAGEMENT = request.app.state.config.ENABLE_LDAP_GROUP_MANAGEMENT
|
||||||
search_base=LDAP_SEARCH_BASE,
|
LDAP_ATTRIBUTE_FOR_GROUPS = request.app.state.config.LDAP_ATTRIBUTE_FOR_GROUPS
|
||||||
search_filter=f"(&({LDAP_ATTRIBUTE_FOR_USERNAME}={escape_filter_chars(form_data.user.lower())}){LDAP_SEARCH_FILTERS})",
|
|
||||||
attributes=[
|
search_attributes = [
|
||||||
f"{LDAP_ATTRIBUTE_FOR_USERNAME}",
|
f"{LDAP_ATTRIBUTE_FOR_USERNAME}",
|
||||||
f"{LDAP_ATTRIBUTE_FOR_MAIL}",
|
f"{LDAP_ATTRIBUTE_FOR_MAIL}",
|
||||||
"cn",
|
"cn",
|
||||||
],
|
]
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
log.info(f"LDAP search attributes: {search_attributes}")
|
||||||
|
|
||||||
|
search_success = connection_app.search(
|
||||||
|
search_base=LDAP_SEARCH_BASE,
|
||||||
|
search_filter=f"(&({LDAP_ATTRIBUTE_FOR_USERNAME}={escape_filter_chars(form_data.user.lower())}){LDAP_SEARCH_FILTERS})",
|
||||||
|
attributes=search_attributes,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not search_success or not connection_app.entries:
|
if not search_success or not connection_app.entries:
|
||||||
@ -258,6 +269,60 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
|
|||||||
cn = str(entry["cn"])
|
cn = str(entry["cn"])
|
||||||
user_dn = entry.entry_dn
|
user_dn = entry.entry_dn
|
||||||
|
|
||||||
|
user_groups = []
|
||||||
|
if ENABLE_LDAP_GROUP_MANAGEMENT and LDAP_ATTRIBUTE_FOR_GROUPS in entry:
|
||||||
|
group_dns = entry[LDAP_ATTRIBUTE_FOR_GROUPS]
|
||||||
|
log.info(f"LDAP raw group DNs for user {username}: {group_dns}")
|
||||||
|
|
||||||
|
if group_dns:
|
||||||
|
log.info(f"LDAP group_dns original: {group_dns}")
|
||||||
|
log.info(f"LDAP group_dns type: {type(group_dns)}")
|
||||||
|
log.info(f"LDAP group_dns length: {len(group_dns)}")
|
||||||
|
|
||||||
|
if hasattr(group_dns, 'value'):
|
||||||
|
group_dns = group_dns.value
|
||||||
|
log.info(f"Extracted .value property: {group_dns}")
|
||||||
|
elif hasattr(group_dns, '__iter__') and not isinstance(group_dns, (str, bytes)):
|
||||||
|
group_dns = list(group_dns)
|
||||||
|
log.info(f"Converted to list: {group_dns}")
|
||||||
|
elif not isinstance(group_dns, list):
|
||||||
|
group_dns = [group_dns]
|
||||||
|
|
||||||
|
if isinstance(group_dns, list):
|
||||||
|
group_dns = [str(item) for item in group_dns]
|
||||||
|
else:
|
||||||
|
group_dns = [str(group_dns)]
|
||||||
|
|
||||||
|
log.info(f"LDAP group_dns after processing - type: {type(group_dns)}, length: {len(group_dns)}")
|
||||||
|
|
||||||
|
for i, group_dn in enumerate(group_dns):
|
||||||
|
group_dn_str = str(group_dn)
|
||||||
|
log.info(f"Processing group DN #{i+1}: {group_dn_str}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
cn_part = None
|
||||||
|
dn_parts = group_dn_str.split(',')
|
||||||
|
log.debug(f"DN parts: {dn_parts}")
|
||||||
|
|
||||||
|
for part in dn_parts:
|
||||||
|
part = part.strip()
|
||||||
|
if part.upper().startswith('CN='):
|
||||||
|
cn_part = part[3:]
|
||||||
|
break
|
||||||
|
|
||||||
|
if cn_part:
|
||||||
|
user_groups.append(cn_part)
|
||||||
|
else:
|
||||||
|
log.warning(f"Could not extract CN from group DN: {group_dn_str}")
|
||||||
|
except Exception as e:
|
||||||
|
log.warning(f"Failed to extract group name from DN {group_dn_str}: {e}")
|
||||||
|
|
||||||
|
log.info(f"LDAP groups for user {username}: {user_groups} (total: {len(user_groups)})")
|
||||||
|
else:
|
||||||
|
log.info(f"No groups found for user {username}")
|
||||||
|
elif ENABLE_LDAP_GROUP_MANAGEMENT:
|
||||||
|
log.warning(f"LDAP Group Management enabled but {LDAP_ATTRIBUTE_FOR_GROUPS} attribute not found in user entry")
|
||||||
|
|
||||||
if username == form_data.user.lower():
|
if username == form_data.user.lower():
|
||||||
connection_user = Connection(
|
connection_user = Connection(
|
||||||
server,
|
server,
|
||||||
@ -333,6 +398,29 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
|
|||||||
user.id, request.app.state.config.USER_PERMISSIONS
|
user.id, request.app.state.config.USER_PERMISSIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ENABLE_LDAP_GROUP_MANAGEMENT and user_groups and request.app.state.config.ENABLE_LDAP_GROUP_CREATION:
|
||||||
|
from open_webui.models.groups import GroupForm
|
||||||
|
existing_groups = Groups.get_groups()
|
||||||
|
existing_group_names = [grp.name for grp in existing_groups]
|
||||||
|
log.info(f"Existing groups: {existing_group_names}")
|
||||||
|
|
||||||
|
for i, g in enumerate(user_groups):
|
||||||
|
if not any(grp.name == g for grp in existing_groups):
|
||||||
|
try:
|
||||||
|
Groups.insert_new_group(user.id, GroupForm(name=g, description=f"{LDAP_SERVER_LABEL}"))
|
||||||
|
log.info(f"Successfully created group '{g}'")
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to create group '{g}': {e}")
|
||||||
|
else:
|
||||||
|
log.info(f"Group {g} already exists")
|
||||||
|
|
||||||
|
if ENABLE_LDAP_GROUP_MANAGEMENT and user_groups and user.role != "admin":
|
||||||
|
try:
|
||||||
|
Groups.sync_user_groups_by_group_names(user.id, user_groups)
|
||||||
|
log.info(f"Successfully synced groups for user {user.id}: {user_groups}")
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to sync groups for user {user.id}: {e}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"token": token,
|
"token": token,
|
||||||
"token_type": "Bearer",
|
"token_type": "Bearer",
|
||||||
|
@ -60,7 +60,7 @@ def get_permissions(
|
|||||||
|
|
||||||
# Combine permissions from all user groups
|
# Combine permissions from all user groups
|
||||||
for group in user_groups:
|
for group in user_groups:
|
||||||
group_permissions = group.permissions
|
group_permissions = group.permissions or {}
|
||||||
permissions = combine_permissions(permissions, group_permissions)
|
permissions = combine_permissions(permissions, group_permissions)
|
||||||
|
|
||||||
# Ensure all fields from default_permissions are present and filled in
|
# Ensure all fields from default_permissions are present and filled in
|
||||||
|
Loading…
Reference in New Issue
Block a user