mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat: 添加 OAuth 认证支持及相关改进
- 新增 OAuth 模块,支持 LinuxDo/GitHub/Google 等第三方登录 - 用户邮箱改为可选字段,支持无邮箱注册 - 新增模块配置验证状态 (config_validated/config_error) - 系统设置界面改为分块独立保存 - 用户设置新增 OAuth 绑定管理和首次密码设置 - 登录界面支持 OAuth 按钮展示 - 邮箱验证设置移至邮件设置页面
This commit is contained in:
@@ -10,10 +10,12 @@ from src.core.modules.base import ModuleDefinition
|
||||
|
||||
# 导入所有模块定义
|
||||
from src.modules.ldap import ldap_module
|
||||
from src.modules.oauth import oauth_module
|
||||
|
||||
# 所有模块列表
|
||||
ALL_MODULES: List[ModuleDefinition] = [
|
||||
ldap_module,
|
||||
oauth_module,
|
||||
]
|
||||
|
||||
__all__ = ["ALL_MODULES"]
|
||||
|
||||
@@ -4,6 +4,8 @@ LDAP 认证模块
|
||||
提供 LDAP/Active Directory 用户认证支持
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from src.core.modules.base import (
|
||||
ModuleCategory,
|
||||
ModuleDefinition,
|
||||
@@ -11,6 +13,9 @@ from src.core.modules.base import (
|
||||
ModuleMetadata,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
def _get_router():
|
||||
"""延迟导入路由(避免启动时加载重依赖)"""
|
||||
@@ -26,6 +31,46 @@ async def _health_check() -> ModuleHealth:
|
||||
return ModuleHealth.UNKNOWN
|
||||
|
||||
|
||||
def _validate_config(db: "Session") -> Tuple[bool, str]:
|
||||
"""
|
||||
验证 LDAP 配置是否可以启用模块
|
||||
|
||||
检查项:
|
||||
1. 配置是否存在
|
||||
2. 必填字段是否完整
|
||||
3. 绑定密码是否可解密
|
||||
|
||||
注意:不在此处执行连接测试,因为 validate_config 会在每次查询模块状态时调用,
|
||||
同步阻塞等待 LDAP 服务器响应会严重影响性能。连接测试应在专门的测试接口中进行。
|
||||
"""
|
||||
from src.core.crypto import crypto_service
|
||||
from src.models.database import LDAPConfig
|
||||
|
||||
config = db.query(LDAPConfig).first()
|
||||
if not config:
|
||||
return False, "请先配置 LDAP 连接信息"
|
||||
|
||||
# 检查必填字段
|
||||
if not config.server_url:
|
||||
return False, "请配置 LDAP 服务器地址"
|
||||
if not config.bind_dn:
|
||||
return False, "请配置绑定 DN"
|
||||
if not config.base_dn:
|
||||
return False, "请配置搜索基准 DN"
|
||||
if not config.bind_password_encrypted:
|
||||
return False, "请配置绑定密码"
|
||||
|
||||
# 尝试解密密码(仅验证可解密,不执行连接测试)
|
||||
try:
|
||||
bind_password = crypto_service.decrypt(config.bind_password_encrypted)
|
||||
if not bind_password:
|
||||
return False, "绑定密码为空,请重新设置"
|
||||
except Exception:
|
||||
return False, "绑定密码解密失败,请重新设置"
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
# LDAP 模块定义
|
||||
ldap_module = ModuleDefinition(
|
||||
metadata=ModuleMetadata(
|
||||
@@ -47,4 +92,5 @@ ldap_module = ModuleDefinition(
|
||||
),
|
||||
router_factory=_get_router,
|
||||
health_check=_health_check,
|
||||
validate_config=_validate_config,
|
||||
)
|
||||
|
||||
88
src/modules/oauth/__init__.py
Normal file
88
src/modules/oauth/__init__.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
OAuth 认证模块
|
||||
|
||||
提供可配置的 OAuth 登录/绑定能力。
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from src.core.modules.base import (
|
||||
ModuleCategory,
|
||||
ModuleDefinition,
|
||||
ModuleHealth,
|
||||
ModuleMetadata,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
def _get_router():
|
||||
"""延迟导入路由(避免启动时加载重依赖/副作用)。"""
|
||||
# 延迟 discover,避免 alembic/mypy 等场景导入时触发 entry_points 解析
|
||||
from src.services.auth.oauth.registry import get_oauth_provider_registry
|
||||
|
||||
get_oauth_provider_registry().discover_providers()
|
||||
|
||||
from src.api.oauth import router
|
||||
|
||||
return router
|
||||
|
||||
|
||||
async def _health_check() -> ModuleHealth:
|
||||
# v1:不做外部网络探测,避免启动时阻塞
|
||||
return ModuleHealth.UNKNOWN
|
||||
|
||||
|
||||
def _validate_config(db: "Session") -> Tuple[bool, str]:
|
||||
"""
|
||||
验证 OAuth 配置是否可以启用模块
|
||||
|
||||
检查项:
|
||||
1. 至少有一个已启用的 Provider 配置
|
||||
2. 已启用的 Provider 必须有 client_id 和 client_secret
|
||||
"""
|
||||
from src.models.database import OAuthProvider
|
||||
|
||||
# 查找所有已启用的 Provider
|
||||
enabled_providers = (
|
||||
db.query(OAuthProvider)
|
||||
.filter(OAuthProvider.is_enabled.is_(True))
|
||||
.all()
|
||||
)
|
||||
|
||||
if not enabled_providers:
|
||||
return False, "请先配置并启用至少一个 OAuth Provider"
|
||||
|
||||
# 检查每个已启用的 Provider 配置完整性
|
||||
for provider in enabled_providers:
|
||||
if not provider.client_id:
|
||||
return False, f"Provider [{provider.display_name}] 未配置 Client ID"
|
||||
if not provider.client_secret_encrypted:
|
||||
return False, f"Provider [{provider.display_name}] 未配置 Client Secret"
|
||||
if not provider.redirect_uri:
|
||||
return False, f"Provider [{provider.display_name}] 未配置回调地址"
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
oauth_module = ModuleDefinition(
|
||||
metadata=ModuleMetadata(
|
||||
name="oauth",
|
||||
display_name="OAuth 登录",
|
||||
description="支持通过第三方 OAuth Provider 登录/绑定账号",
|
||||
category=ModuleCategory.AUTH,
|
||||
env_key="OAUTH_AVAILABLE",
|
||||
default_available=True,
|
||||
required_packages=["httpx", "redis"],
|
||||
api_prefix="/api/oauth",
|
||||
admin_route="/admin/oauth",
|
||||
admin_menu_icon="Key",
|
||||
admin_menu_group="system",
|
||||
admin_menu_order=55,
|
||||
),
|
||||
router_factory=_get_router,
|
||||
health_check=_health_check,
|
||||
validate_config=_validate_config,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user