122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
"""Synchronisation von Azure AD Gruppenmitgliedern → CardSync User-Tabelle.
|
|
|
|
Holt alle Mitglieder einer Azure AD Gruppe via Microsoft Graph (Application Permissions)
|
|
und legt sie als User in CardSync an. Bestehende User werden aktualisiert,
|
|
nicht mehr in der Gruppe vorhandene User werden NICHT automatisch gelöscht
|
|
(nur deren Sync deaktiviert, damit es kein Datenverlust gibt).
|
|
"""
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
from sqlalchemy.orm import Session
|
|
|
|
from models import User, AzureGroupConfig
|
|
from ms_graph import get_group_info, get_group_members
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def sync_group_members(db: Session) -> dict:
|
|
"""Holt alle Gruppenmitglieder und legt User in der DB an / aktualisiert sie."""
|
|
config = db.query(AzureGroupConfig).first()
|
|
if not config:
|
|
return {"status": "error", "message": "Keine Azure-Gruppen-Konfiguration vorhanden"}
|
|
|
|
started = datetime.now(timezone.utc)
|
|
|
|
try:
|
|
# Gruppen-Info aktualisieren
|
|
group_info = await get_group_info(config.group_id)
|
|
if group_info:
|
|
config.group_name = group_info.get("displayName", config.group_name)
|
|
|
|
members = await get_group_members(config.group_id)
|
|
logger.info(f"Azure AD: {len(members)} Mitglieder in Gruppe '{config.group_name}' gefunden")
|
|
|
|
created = 0
|
|
updated = 0
|
|
deactivated = 0
|
|
|
|
# Set für die Erkennung entfernter Mitglieder
|
|
current_member_ids = set()
|
|
|
|
for m in members:
|
|
ms_user_id = m.get("id")
|
|
email = (m.get("mail") or m.get("userPrincipalName") or "").lower()
|
|
display_name = m.get("displayName", "")
|
|
|
|
if not ms_user_id or not email:
|
|
continue
|
|
|
|
current_member_ids.add(ms_user_id)
|
|
|
|
user = db.query(User).filter(User.ms_user_id == ms_user_id).first()
|
|
if not user:
|
|
# Mit E-Mail als Fallback suchen
|
|
user = db.query(User).filter(User.email == email).first()
|
|
|
|
if user:
|
|
# Existierender User → Stammdaten aktualisieren
|
|
if user.ms_user_id != ms_user_id:
|
|
user.ms_user_id = ms_user_id
|
|
if user.email != email:
|
|
user.email = email
|
|
if user.display_name != display_name and display_name:
|
|
user.display_name = display_name
|
|
|
|
# Auto-Aktivierung NUR für reine Group-Import-User
|
|
# OAuth-User behalten ihre individuellen Einstellungen
|
|
if user.source == "group_import":
|
|
if not user.sync_enabled and config.default_sync_enabled:
|
|
user.sync_enabled = True
|
|
|
|
user.updated_at = datetime.now(timezone.utc)
|
|
updated += 1
|
|
else:
|
|
# Neuer User
|
|
new_user = User(
|
|
ms_user_id=ms_user_id,
|
|
email=email,
|
|
display_name=display_name,
|
|
source="group_import",
|
|
sync_enabled=config.default_sync_enabled,
|
|
carddav_folder=config.default_carddav_folder,
|
|
sync_interval_minutes=config.default_sync_interval_minutes,
|
|
)
|
|
db.add(new_user)
|
|
created += 1
|
|
logger.info(f"Neuer User aus Gruppe importiert: {email}")
|
|
|
|
# User die nicht mehr in der Gruppe sind → Sync deaktivieren
|
|
# (nur für group_import User, OAuth-User nicht anfassen)
|
|
all_imported = db.query(User).filter(User.source == "group_import").all()
|
|
for u in all_imported:
|
|
if u.ms_user_id not in current_member_ids and u.sync_enabled:
|
|
u.sync_enabled = False
|
|
u.last_sync_message = "Aus Azure AD Gruppe entfernt — Sync automatisch deaktiviert"
|
|
u.updated_at = datetime.now(timezone.utc)
|
|
deactivated += 1
|
|
logger.info(f"Sync deaktiviert für {u.email} (nicht mehr in Gruppe)")
|
|
|
|
config.last_member_sync_at = started
|
|
config.last_member_sync_status = "success"
|
|
config.last_member_sync_message = (
|
|
f"{created} neu, {updated} aktualisiert, {deactivated} deaktiviert"
|
|
)
|
|
db.commit()
|
|
|
|
return {
|
|
"status": "success",
|
|
"message": config.last_member_sync_message,
|
|
"created": created,
|
|
"updated": updated,
|
|
"deactivated": deactivated,
|
|
"total_members": len(members),
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Group member sync failed: {e}", exc_info=True)
|
|
config.last_member_sync_at = started
|
|
config.last_member_sync_status = "error"
|
|
config.last_member_sync_message = str(e)
|
|
db.commit()
|
|
return {"status": "error", "message": str(e)}
|