Initial commit – TimeMaster Zeiterfassung & HR-Tool
Stand: agent-06 (Audit-Log), agent-05 (Krankmeldung), agent-07 Phase 1 (Personalnummer), Busylight-Pull-Integration, TOTP/2FA, Abwesenheiten, Zeiterfassung, Kiosk-Grundgerüst. Migrations 0001–0023 deployed auf 192.168.1.137 + .164. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
"""LDAP configuration and sync endpoints.
|
||||
|
||||
All endpoints require COMPANY_ADMIN or SUPER_ADMIN role.
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.dependencies import require_role
|
||||
from app.models.ldap_config import LdapConfig
|
||||
from app.models.user import User, UserRole
|
||||
|
||||
_admin_roles = (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)
|
||||
from app.schemas.ldap import (
|
||||
LdapConfigCreate,
|
||||
LdapConfigOut,
|
||||
LdapConfigUpdate,
|
||||
LdapSyncRequest,
|
||||
LdapSyncResult,
|
||||
LdapTestResult,
|
||||
LdapUserPreview,
|
||||
)
|
||||
from app.services.ldap_service import decrypt_password, encrypt_password, ldap_service
|
||||
|
||||
router = APIRouter(prefix="/ldap", tags=["LDAP"])
|
||||
|
||||
|
||||
@router.get("/config", response_model=LdapConfigOut | None)
|
||||
async def get_ldap_config(
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
return await ldap_service.get_config(current_user.company_id, db)
|
||||
|
||||
|
||||
@router.post("/config", response_model=LdapConfigOut)
|
||||
async def create_ldap_config(
|
||||
data: LdapConfigCreate,
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
existing = await ldap_service.get_config(current_user.company_id, db)
|
||||
if existing:
|
||||
# Update instead of duplicate
|
||||
return await _apply_update(existing, data.model_dump(), db)
|
||||
|
||||
cfg = LdapConfig(
|
||||
company_id=current_user.company_id,
|
||||
enabled=data.enabled,
|
||||
host=data.host,
|
||||
port=data.port,
|
||||
use_ssl=data.use_ssl,
|
||||
use_tls=data.use_tls,
|
||||
bind_dn=data.bind_dn,
|
||||
bind_password_encrypted=encrypt_password(data.bind_password),
|
||||
base_dn=data.base_dn,
|
||||
user_search_filter=data.user_search_filter,
|
||||
attr_email=data.attr_email,
|
||||
attr_firstname=data.attr_firstname,
|
||||
attr_lastname=data.attr_lastname,
|
||||
attr_username=data.attr_username,
|
||||
attr_department=data.attr_department,
|
||||
)
|
||||
db.add(cfg)
|
||||
await db.commit()
|
||||
await db.refresh(cfg)
|
||||
return cfg
|
||||
|
||||
|
||||
@router.patch("/config", response_model=LdapConfigOut)
|
||||
async def update_ldap_config(
|
||||
data: LdapConfigUpdate,
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
cfg = await ldap_service.get_config_or_404(current_user.company_id, db)
|
||||
return await _apply_update(cfg, data.model_dump(exclude_none=True), db)
|
||||
|
||||
|
||||
@router.post("/test", response_model=LdapTestResult)
|
||||
async def test_ldap_connection(
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
cfg = await ldap_service.get_config_or_404(current_user.company_id, db)
|
||||
result = ldap_service.test_connection(cfg)
|
||||
return LdapTestResult(success=result.success, message=result.message)
|
||||
|
||||
|
||||
@router.get("/preview", response_model=list[LdapUserPreview])
|
||||
async def preview_ldap_users(
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Returns first 50 users found in LDAP (for preview before sync)."""
|
||||
cfg = await ldap_service.get_config_or_404(current_user.company_id, db)
|
||||
raw_users = ldap_service.search_users(cfg)
|
||||
previews = []
|
||||
for u in raw_users[:50]:
|
||||
previews.append(LdapUserPreview(
|
||||
dn=u.get("dn", ""),
|
||||
email=str(u.get(cfg.attr_email, "") or "").lower(),
|
||||
first_name=str(u.get(cfg.attr_firstname, "") or ""),
|
||||
last_name=str(u.get(cfg.attr_lastname, "") or ""),
|
||||
department=str(u.get(cfg.attr_department, "") or "") if cfg.attr_department else None,
|
||||
))
|
||||
return previews
|
||||
|
||||
|
||||
@router.post("/sync", response_model=LdapSyncResult)
|
||||
async def sync_ldap_users(
|
||||
data: LdapSyncRequest,
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
cfg = await ldap_service.get_config_or_404(current_user.company_id, db)
|
||||
result = await ldap_service.sync_users(cfg, db, default_role=data.default_role)
|
||||
return LdapSyncResult(
|
||||
created=result.created,
|
||||
updated=result.updated,
|
||||
deactivated=result.deactivated,
|
||||
errors=result.errors,
|
||||
)
|
||||
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
async def _apply_update(cfg: LdapConfig, updates: dict, db: AsyncSession) -> LdapConfig:
|
||||
for field, value in updates.items():
|
||||
if field == "bind_password" and value:
|
||||
cfg.bind_password_encrypted = encrypt_password(value)
|
||||
elif hasattr(cfg, field):
|
||||
setattr(cfg, field, value)
|
||||
await db.commit()
|
||||
await db.refresh(cfg)
|
||||
return cfg
|
||||
Reference in New Issue
Block a user