Merge a1b074c684
into 04a0ae3aa9
This commit is contained in:
commit
a06088ee10
@ -445,4 +445,18 @@ CREATE_TIDB_SERVICE_JOB_ENABLED=false
|
|||||||
# Maximum number of submitted thread count in a ThreadPool for parallel node execution
|
# Maximum number of submitted thread count in a ThreadPool for parallel node execution
|
||||||
MAX_SUBMIT_COUNT=100
|
MAX_SUBMIT_COUNT=100
|
||||||
# Lockout duration in seconds
|
# Lockout duration in seconds
|
||||||
LOGIN_LOCKOUT_DURATION=86400
|
LOGIN_LOCKOUT_DURATION=86400
|
||||||
|
|
||||||
|
# Enable ldap authentication login
|
||||||
|
LDAP_ENABLED=false
|
||||||
|
AUTH_LDAP_SERVER_URI=ldap://127.0.0.1:389
|
||||||
|
AUTH_LDAP_BIND_DN=CN=ArcherMaster,CN=Users,DC=dify,DC=com
|
||||||
|
AUTH_LDAP_BIND_PASSWORD=dify@123456
|
||||||
|
AUTH_LDAP_SEARCH_BASE_DN=OU=dify技术有限公司,DC=dify,DC=com
|
||||||
|
AUTH_LDAP_USER_FILTER=(mail=%(user)s)
|
||||||
|
AUTH_LDAP_USER_ATTR_MAP={"first_name": "uid", "last_name": "cn", "email": "mail"}
|
||||||
|
CACHE_LDAP_USER_KEY=LDAP_USER_INFO_DATA
|
||||||
|
CACHE_LDAP_USER_EX=86400
|
||||||
|
LDAP_DEFAULT_ROLE=normal
|
||||||
|
LDAP_CONN_TIMEOUT=10
|
||||||
|
LDAP_POOL_SIZE=10
|
@ -47,6 +47,7 @@ def initialize_extensions(app: DifyApp):
|
|||||||
ext_database,
|
ext_database,
|
||||||
ext_hosting_provider,
|
ext_hosting_provider,
|
||||||
ext_import_modules,
|
ext_import_modules,
|
||||||
|
ext_ldap,
|
||||||
ext_logging,
|
ext_logging,
|
||||||
ext_login,
|
ext_login,
|
||||||
ext_mail,
|
ext_mail,
|
||||||
@ -61,6 +62,7 @@ def initialize_extensions(app: DifyApp):
|
|||||||
)
|
)
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
|
ext_ldap,
|
||||||
ext_timezone,
|
ext_timezone,
|
||||||
ext_logging,
|
ext_logging,
|
||||||
ext_warnings,
|
ext_warnings,
|
||||||
|
@ -8,6 +8,7 @@ from .deploy import DeploymentConfig
|
|||||||
from .enterprise import EnterpriseFeatureConfig
|
from .enterprise import EnterpriseFeatureConfig
|
||||||
from .extra import ExtraServiceConfig
|
from .extra import ExtraServiceConfig
|
||||||
from .feature import FeatureConfig
|
from .feature import FeatureConfig
|
||||||
|
from .ldap import AuthenticationConfig
|
||||||
from .middleware import MiddlewareConfig
|
from .middleware import MiddlewareConfig
|
||||||
from .packaging import PackagingInfo
|
from .packaging import PackagingInfo
|
||||||
from .remote_settings_sources import RemoteSettingsSource, RemoteSettingsSourceConfig, RemoteSettingsSourceName
|
from .remote_settings_sources import RemoteSettingsSource, RemoteSettingsSourceConfig, RemoteSettingsSourceName
|
||||||
@ -49,6 +50,8 @@ class RemoteSettingsSourceFactory(PydanticBaseSettingsSource):
|
|||||||
|
|
||||||
|
|
||||||
class DifyConfig(
|
class DifyConfig(
|
||||||
|
# Authentication configs
|
||||||
|
AuthenticationConfig,
|
||||||
# Packaging info
|
# Packaging info
|
||||||
PackagingInfo,
|
PackagingInfo,
|
||||||
# Deployment configs
|
# Deployment configs
|
||||||
|
74
api/configs/ldap/__init__.py
Normal file
74
api/configs/ldap/__init__.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
@File : __init__.py.py
|
||||||
|
@Time : 2025/3/6 {TIME}
|
||||||
|
@Author : xxlaila
|
||||||
|
@Software: dify
|
||||||
|
"""
|
||||||
|
from pydantic import Field
|
||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationConfig(BaseSettings):
|
||||||
|
"""LDAP authentication related configuration"""
|
||||||
|
# LDAP Authentication
|
||||||
|
LDAP_ENABLED: bool = Field(
|
||||||
|
False,
|
||||||
|
description="Whether to enable LDAP authentication",
|
||||||
|
validation_alias="LDAP_ENABLED"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_SERVER_URI: str = Field(
|
||||||
|
"ldap://localhost",
|
||||||
|
description="LDAP server address",
|
||||||
|
validation_alias="AUTH_LDAP_SERVER_URI"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_BIND_DN: str = Field(
|
||||||
|
"cn=admin,dc=example,dc=com",
|
||||||
|
description="LDAP Bind DN",
|
||||||
|
validation_alias="AUTH_LDAP_BIND_DN"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_BIND_PASSWORD: str = Field(
|
||||||
|
"<PASSWORD>",
|
||||||
|
description="LDAP bind password",
|
||||||
|
validation_alias="AUTH_LDAP_BIND_PASSWORD"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_SEARCH_BASE_DN: str = Field(
|
||||||
|
default="OU=Limited Company,DC=example,DC=com",
|
||||||
|
description="LDAP search base DN",
|
||||||
|
validation_alias="AUTH_LDAP_SEARCH_BASE_DN"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_USER_FILTER: str = Field(
|
||||||
|
"(objectClass=person)",
|
||||||
|
description="LDAP User Filter",
|
||||||
|
validation_alias="AUTH_LDAP_USER_FILTER"
|
||||||
|
)
|
||||||
|
AUTH_LDAP_USER_ATTR_MAP: dict = Field(
|
||||||
|
{"first_name": "uid", "last_name": "cn", "email": "mail"},
|
||||||
|
description="Mapping of LDAP attributes to user models",
|
||||||
|
validation_alias="AUTH_LDAP_USER_ATTR_MAP"
|
||||||
|
)
|
||||||
|
CACHE_LDAP_USER_KEY: str = Field(
|
||||||
|
# Cache LDAP user information
|
||||||
|
"ldap_user",
|
||||||
|
description="Key for caching LDAP user information",
|
||||||
|
validation_alias="CACHE_LDAP_USER_KEY"
|
||||||
|
)
|
||||||
|
CACHE_LDAP_USER_EX: int = Field(
|
||||||
|
86400,
|
||||||
|
description="Expiration time of cached LDAP user information",
|
||||||
|
validation_alias="CACHE_LDAP_USER_EX"
|
||||||
|
)
|
||||||
|
LDAP_DEFAULT_ROLE: str = Field(
|
||||||
|
default="normal",
|
||||||
|
description="Default Roles",
|
||||||
|
validation_alias="LDAP_DEFAULT_ROLE"
|
||||||
|
)
|
||||||
|
LDAP_CONN_TIMEOUT: int = Field(
|
||||||
|
default=10,
|
||||||
|
description="LDAP connection timeout",
|
||||||
|
validation_alias="LDAP_CONN_TIMEOUT"
|
||||||
|
)
|
||||||
|
LDAP_POOL_SIZE: int = Field(
|
||||||
|
default=10,
|
||||||
|
description="LDAP connection pool size",
|
||||||
|
validation_alias="LDAP_POOL_SIZE"
|
||||||
|
)
|
63
api/extensions/ext_ldap.py
Normal file
63
api/extensions/ext_ldap.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
@File : ext_ldap.py
|
||||||
|
@Time : 2025/3/5 {TIME}
|
||||||
|
@Author : xxlaila
|
||||||
|
@Software: dify
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from flask_ldap3_login import LDAP3LoginManager
|
||||||
|
|
||||||
|
from configs import dify_config
|
||||||
|
from dify_app import DifyApp
|
||||||
|
|
||||||
|
|
||||||
|
def is_enabled():
|
||||||
|
return getattr(dify_config, 'LDAP_ENABLED', False)
|
||||||
|
|
||||||
|
def init_app(app: DifyApp):
|
||||||
|
"""Initialize LDAP authentication integration"""
|
||||||
|
if not is_enabled():
|
||||||
|
app.ldap_manager = None # Explicitly set the manager to None
|
||||||
|
logging.info("LDAP authentication is disabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parsing User Attribute Mapping
|
||||||
|
if isinstance(dify_config.AUTH_LDAP_USER_ATTR_MAP, str):
|
||||||
|
ldap_user_attr_map = json.loads(dify_config.AUTH_LDAP_USER_ATTR_MAP)
|
||||||
|
else:
|
||||||
|
ldap_user_attr_map = dify_config.AUTH_LDAP_USER_ATTR_MAP
|
||||||
|
|
||||||
|
# Setting up LDAP configuration
|
||||||
|
app.config.update({
|
||||||
|
"LDAP_HOST": dify_config.AUTH_LDAP_SERVER_URI,
|
||||||
|
"LDAP_BASE_DN": dify_config.AUTH_LDAP_SEARCH_BASE_DN,
|
||||||
|
"LDAP_BIND_DN": dify_config.AUTH_LDAP_BIND_DN,
|
||||||
|
"LDAP_BIND_PASSWORD": dify_config.AUTH_LDAP_BIND_PASSWORD,
|
||||||
|
"LDAP_USER_FILTER": dify_config.AUTH_LDAP_USER_FILTER,
|
||||||
|
"LDAP_USER_RDN_ATTR": "uid",
|
||||||
|
"LDAP_USER_LOGIN_ATTR": "uid",
|
||||||
|
"LDAP_USER_SEARCH_SCOPE": "SUBTREE",
|
||||||
|
"LDAP_USER_MAPPING": ldap_user_attr_map,
|
||||||
|
"LDAP_DEFAULT_ROLE": dify_config.LDAP_DEFAULT_ROLE,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# Initializing the LDAP Manager
|
||||||
|
ldap_manager = LDAP3LoginManager()
|
||||||
|
ldap_manager.init_app(app)
|
||||||
|
|
||||||
|
# Mount the LDAP manager into the app
|
||||||
|
app.ldap_manager = ldap_manager
|
||||||
|
# Confirm that the mount was successful
|
||||||
|
logging.info(f"LDAP manager mounted: {hasattr(app, 'ldap_manager')}")
|
||||||
|
|
||||||
|
|
||||||
|
# Configuring Logging
|
||||||
|
if app.debug:
|
||||||
|
app.logger.info("LDAP configuration loaded:")
|
||||||
|
app.logger.info(f"Server: {app.config['LDAP_HOST']}")
|
||||||
|
app.logger.info(f"Base DN: {app.config['LDAP_BASE_DN']}")
|
||||||
|
|
||||||
|
return ldap_manager
|
@ -12,6 +12,10 @@ from services.account_service import AccountService
|
|||||||
|
|
||||||
login_manager = flask_login.LoginManager()
|
login_manager = flask_login.LoginManager()
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
"""Support loading LDAP users from session"""
|
||||||
|
return AccountService.load_user(user_id)
|
||||||
|
|
||||||
# Flask-Login configuration
|
# Flask-Login configuration
|
||||||
@login_manager.request_loader
|
@login_manager.request_loader
|
||||||
|
25
api/poetry.lock
generated
25
api/poetry.lock
generated
@ -2168,6 +2168,7 @@ files = [
|
|||||||
brotli = {version = "*", markers = "platform_python_implementation != \"PyPy\""}
|
brotli = {version = "*", markers = "platform_python_implementation != \"PyPy\""}
|
||||||
brotlicffi = {version = "*", markers = "platform_python_implementation == \"PyPy\""}
|
brotlicffi = {version = "*", markers = "platform_python_implementation == \"PyPy\""}
|
||||||
flask = "*"
|
flask = "*"
|
||||||
|
ldap3 = ">=2.0.7"
|
||||||
zstandard = [
|
zstandard = [
|
||||||
{version = "*", markers = "platform_python_implementation != \"PyPy\""},
|
{version = "*", markers = "platform_python_implementation != \"PyPy\""},
|
||||||
{version = "*", extras = ["cffi"], markers = "platform_python_implementation == \"PyPy\""},
|
{version = "*", extras = ["cffi"], markers = "platform_python_implementation == \"PyPy\""},
|
||||||
@ -2188,6 +2189,18 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Flask = ">=0.9"
|
Flask = ">=0.9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flask-ldap3-login"
|
||||||
|
version = "1.0.2"
|
||||||
|
description = "LDAP Support for Flask"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "flask_ldap3_login-1.0.2-py3-none-any.whl", hash = "sha256:72368ea58b5faac4935d1f3e5ec1fcfdce51cc16adbec41a74c834247422ac61"},
|
||||||
|
{file = "flask_ldap3_login-1.0.2.tar.gz", hash = "sha256:969d2e4263aa0908383174d9700e03198a5eb3db483b26a18f9ea634032cbdd4"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flask-login"
|
name = "flask-login"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@ -3949,6 +3962,18 @@ requests-toolbelt = ">=1.0.0,<2.0.0"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"]
|
langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ldap3"
|
||||||
|
version = "2.9.1"
|
||||||
|
description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
groups = ["main"]
|
||||||
|
files = [
|
||||||
|
{file = "ldap3-2.9.1-py2.py3-none-any.whl", hash = "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70"},
|
||||||
|
{file = "ldap3-2.9.1.tar.gz", hash = "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "levenshtein"
|
name = "levenshtein"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
|
@ -83,6 +83,8 @@ yarl = "~1.18.3"
|
|||||||
# Related transparent dependencies with pinned version
|
# Related transparent dependencies with pinned version
|
||||||
# required by main implementations
|
# required by main implementations
|
||||||
############################################################
|
############################################################
|
||||||
|
flask-ldap3-login = "^1.0.2"
|
||||||
|
ldap3 = "^2.9.1"
|
||||||
[tool.poetry.group.indirect.dependencies]
|
[tool.poetry.group.indirect.dependencies]
|
||||||
kaleido = "0.2.1"
|
kaleido = "0.2.1"
|
||||||
rank-bm25 = "~0.2.2"
|
rank-bm25 = "~0.2.2"
|
||||||
|
@ -8,6 +8,7 @@ from datetime import UTC, datetime, timedelta
|
|||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from typing import Any, Optional, cast
|
from typing import Any, Optional, cast
|
||||||
|
|
||||||
|
from ldap3 import ALL, Connection, Server
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@ -17,6 +18,7 @@ from configs import dify_config
|
|||||||
from constants.languages import language_timezone_mapping, languages
|
from constants.languages import language_timezone_mapping, languages
|
||||||
from events.tenant_event import tenant_was_created
|
from events.tenant_event import tenant_was_created
|
||||||
from extensions.ext_database import db
|
from extensions.ext_database import db
|
||||||
|
from extensions.ext_ldap import is_enabled
|
||||||
from extensions.ext_redis import redis_client
|
from extensions.ext_redis import redis_client
|
||||||
from libs.helper import RateLimiter, TokenManager
|
from libs.helper import RateLimiter, TokenManager
|
||||||
from libs.passport import PassportService
|
from libs.passport import PassportService
|
||||||
@ -145,11 +147,90 @@ class AccountService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def authenticate(email: str, password: str, invite_token: Optional[str] = None) -> Account:
|
def authenticate(email: str, password: str, invite_token: Optional[str] = None) -> Account:
|
||||||
"""authenticate account with email and password"""
|
"""authenticate account with email and password"""
|
||||||
|
account = None
|
||||||
|
|
||||||
|
def _handle_ldap_user(email: str, password: str) -> Optional[Account]:
|
||||||
|
"""Handle LDAP user authentication"""
|
||||||
|
server = Server(dify_config.AUTH_LDAP_SERVER_URI, get_info=ALL)
|
||||||
|
conn = Connection(
|
||||||
|
server,
|
||||||
|
user=dify_config.AUTH_LDAP_BIND_DN,
|
||||||
|
password=dify_config.AUTH_LDAP_BIND_PASSWORD,
|
||||||
|
receive_timeout=dify_config.LDAP_CONN_TIMEOUT,
|
||||||
|
auto_bind=True
|
||||||
|
)
|
||||||
|
conn.search(
|
||||||
|
search_base=dify_config.AUTH_LDAP_SEARCH_BASE_DN,
|
||||||
|
search_filter=f"(mail={email})",
|
||||||
|
attributes=list(dify_config.AUTH_LDAP_USER_ATTR_MAP.values())
|
||||||
|
)
|
||||||
|
if not conn.entries:
|
||||||
|
logging.warning(f"No LDAP entry found for: {email}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
entry = conn.entries[0]
|
||||||
|
user_dn = entry.entry_dn # Obtain the complete DN of the user
|
||||||
|
# Add password verification steps
|
||||||
|
user_conn = Connection(
|
||||||
|
server,
|
||||||
|
user=user_dn,
|
||||||
|
password=password
|
||||||
|
)
|
||||||
|
if not user_conn.bind():
|
||||||
|
logging.error(f"LDAP password verification failed: {email}")
|
||||||
|
raise AccountPasswordError("Password error")
|
||||||
|
|
||||||
|
display_name = entry.cn.value if entry.cn else email.split('@')[0]
|
||||||
|
account = db.session.query(Account).filter_by(email=email).with_for_update().first()
|
||||||
|
if not account:
|
||||||
|
logging.info(f"Creating new LDAP account for: {email}")
|
||||||
|
return AccountService.create_ldap_user(
|
||||||
|
email=email,
|
||||||
|
name=display_name,
|
||||||
|
ldap_attrs=entry
|
||||||
|
)
|
||||||
|
|
||||||
|
if account.name != display_name:
|
||||||
|
logging.info(f"Updating LDAP account name for: {email}")
|
||||||
|
account.name = display_name
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return account
|
||||||
|
|
||||||
|
# Main certification process
|
||||||
|
if is_enabled():
|
||||||
|
try:
|
||||||
|
# Try LDAP authentication first
|
||||||
|
server = Server(dify_config.AUTH_LDAP_SERVER_URI, get_info=ALL)
|
||||||
|
conn = Connection(
|
||||||
|
server,
|
||||||
|
user=dify_config.AUTH_LDAP_BIND_DN,
|
||||||
|
password=dify_config.AUTH_LDAP_BIND_PASSWORD,
|
||||||
|
receive_timeout=dify_config.LDAP_CONN_TIMEOUT,
|
||||||
|
auto_bind=True
|
||||||
|
)
|
||||||
|
if not conn.bind():
|
||||||
|
logging.error("Failed to bind to LDAP server.")
|
||||||
|
raise ValueError("Failed to bind to LDAP server.")
|
||||||
|
account = _handle_ldap_user(email, password)
|
||||||
|
if account:
|
||||||
|
logging.info(f"Returning account {email} after successful LDAP authentication.")
|
||||||
|
|
||||||
|
return account
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f"LDAP authentication error: {e}")
|
||||||
|
|
||||||
|
# Perform local authentication only if LDAP authentication explicitly fails
|
||||||
account = db.session.query(Account).filter_by(email=email).first()
|
account = db.session.query(Account).filter_by(email=email).first()
|
||||||
if not account:
|
if not account:
|
||||||
|
logging.error(f"Account not found for: {email}")
|
||||||
raise AccountNotFoundError()
|
raise AccountNotFoundError()
|
||||||
|
|
||||||
|
# LDAP users must authenticate via LDAP
|
||||||
|
if account.password is None and account.password_salt is None and not invite_token:
|
||||||
|
logging.error(f"LDAP user {email} attempted local authentication")
|
||||||
|
raise AccountPasswordError("LDAP users must authenticate via LDAP")
|
||||||
|
|
||||||
if account.status == AccountStatus.BANNED.value:
|
if account.status == AccountStatus.BANNED.value:
|
||||||
raise AccountLoginError("Account is banned.")
|
raise AccountLoginError("Account is banned.")
|
||||||
|
|
||||||
@ -173,6 +254,56 @@ class AccountService:
|
|||||||
|
|
||||||
return cast(Account, account)
|
return cast(Account, account)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_ldap_user(email: str, name: str, ldap_attrs: dict) -> Account:
|
||||||
|
"""
|
||||||
|
Create LDAP users and assign default permissions
|
||||||
|
Note: LDAP users do not require a local password, so password and password_stalt are set to None.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# First check if the account already exists
|
||||||
|
account = db.session.query(Account).filter_by(email=email).first()
|
||||||
|
if account:
|
||||||
|
logging.info(f"Account {email} already exists, skipping creation.")
|
||||||
|
else:
|
||||||
|
account = Account(
|
||||||
|
email=email,
|
||||||
|
name=name,
|
||||||
|
status=AccountStatus.ACTIVE.value,
|
||||||
|
password=None, # LDAP user has no local password
|
||||||
|
password_salt=None,
|
||||||
|
interface_language=languages[0]
|
||||||
|
)
|
||||||
|
db.session.add(account)
|
||||||
|
db.session.commit() # Submit the transaction to ensure that the account is available
|
||||||
|
logging.info(f"Local account created successfully for: {email}")
|
||||||
|
|
||||||
|
# Get the default tenant (remove the specific name and only take the first one)
|
||||||
|
default_tenant = db.session.query(Tenant).first()
|
||||||
|
if not default_tenant:
|
||||||
|
default_tenant = TenantService.create_tenant(name="Default Workspace")
|
||||||
|
db.session.commit()
|
||||||
|
logging.info(f"Created default tenant: {default_tenant.id}")
|
||||||
|
|
||||||
|
# Check if the user has joined the tenant
|
||||||
|
existing_member = db.session.query(TenantAccountJoin).filter_by(
|
||||||
|
tenant_id=default_tenant.id, account_id=account.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not existing_member:
|
||||||
|
TenantService.create_tenant_member(tenant=default_tenant, account=account,
|
||||||
|
role="normal")
|
||||||
|
logging.info(f"User {email} added to tenant: {default_tenant.id}")
|
||||||
|
else:
|
||||||
|
logging.info(f"User {email} is already a member of tenant {default_tenant.id}")
|
||||||
|
|
||||||
|
return account # Make sure to return a valid account
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback() # Transaction rollback to prevent database pollution
|
||||||
|
logging.info(f"Failed to create LDAP user or add to tenant: {e}")
|
||||||
|
raise AccountRegisterError("Failed to create LDAP user.")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_account_password(account, password, new_password):
|
def update_account_password(account, password, new_password):
|
||||||
"""update account password"""
|
"""update account password"""
|
||||||
|
Loading…
Reference in New Issue
Block a user