busylight_modular/graph_client.py
Christian Mueller dd9e2f4acd fix: use allow_unencrypted_storage for headless Linux token cache
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 21:15:16 +02:00

99 lines
3.4 KiB
Python

import logging
from azure.identity import (
DeviceCodeCredential,
TokenCachePersistenceOptions,
AuthenticationRecord,
)
from msgraph.core import GraphClient
from database import get_setting, set_setting
logger = logging.getLogger(__name__)
CACHE_OPTIONS = TokenCachePersistenceOptions(
name="busylight", allow_unencrypted_storage=True
)
class GraphAPI:
def __init__(self):
self.client = None
self.credential = None
self._init_client()
def _init_client(self):
client_id = get_setting("azure_client_id")
tenant_id = get_setting("azure_auth_tenant", "common")
scopes = get_setting("azure_scopes").split()
auth_record_json = get_setting("azure_auth_record")
if not client_id or not auth_record_json:
logger.warning("Azure nicht konfiguriert oder nicht authentifiziert")
return
try:
record = AuthenticationRecord.deserialize(auth_record_json)
self.credential = DeviceCodeCredential(
client_id=client_id,
tenant_id=tenant_id,
cache_persistence_options=CACHE_OPTIONS,
authentication_record=record,
)
self.credential.authenticate(tenant_id=tenant_id, scopes=scopes)
self.client = GraphClient(credential=self.credential, scopes=scopes)
logger.info("Graph-Client erfolgreich initialisiert")
except Exception as e:
logger.error("Graph-Client Fehler: %s", e)
self.client = None
def authenticate(self):
"""Trigger device code flow. Must run in terminal."""
client_id = get_setting("azure_client_id")
tenant_id = get_setting("azure_auth_tenant", "common")
scopes = get_setting("azure_scopes").split()
if not client_id:
raise ValueError("Azure Client ID nicht konfiguriert")
self.credential = DeviceCodeCredential(
client_id=client_id,
tenant_id=tenant_id,
cache_persistence_options=CACHE_OPTIONS,
)
record = self.credential.authenticate(tenant_id=tenant_id, scopes=scopes)
set_setting("azure_auth_record", record.serialize())
self.client = GraphClient(credential=self.credential, scopes=scopes)
logger.info("Azure Authentifizierung erfolgreich")
return record
def is_authenticated(self):
return self.client is not None
def get_users(self):
if not self.client:
return []
try:
resp = self.client.get(
"/users?$select=displayName,id,mail&$orderBy=displayName&$top=999"
)
data = resp.json()
users = data.get("value", [])
# Handle pagination
while "@odata.nextLink" in data:
resp = self.client.get(data["@odata.nextLink"])
data = resp.json()
users.extend(data.get("value", []))
return users
except Exception as e:
logger.error("Fehler beim Abrufen der User: %s", e)
return []
def get_presence(self, user_id):
if not self.client:
return None
try:
resp = self.client.get(f"/users/{user_id}/presence")
return resp.json()
except Exception as e:
logger.error("Fehler beim Abrufen der Presence für %s: %s", user_id, e)
return None