First Commit with complete Setup coded by Claude 4.7 Opus
This commit is contained in:
commit
bf1d37de77
19 changed files with 4544 additions and 0 deletions
122
backend/group_sync.py
Normal file
122
backend/group_sync.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
"""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)}
|
||||
Loading…
Add table
Add a link
Reference in a new issue