170 lines
8.4 KiB
Python
170 lines
8.4 KiB
Python
"""
|
|
Overzicht van alle lopende e-mailmachtigingen (Gmail-delegaties) van de school.
|
|
|
|
Google heeft geen lijst "alle delegaties van het domein": je kan ze enkel per mailbox opvragen
|
|
(users.settings.delegates.list, in naam van de eigenaar). Daarom loopt er een achtergrondtaak die
|
|
alle actieve (niet-geschorste) gebruikers afgaat, met een paar parallelle verzoeken.
|
|
Het resultaat komt in de tabel gmail_delegaties en wordt:
|
|
- volledig vernieuwd met de knop op de pagina, of vanzelf als het ouder is dan VERVERS_UREN;
|
|
- meteen bijgewerkt als in PIVOT een machtiging toegevoegd of ingetrokken wordt, of als je een mailbox opent.
|
|
"""
|
|
import sys
|
|
import threading
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from datetime import datetime
|
|
|
|
from database import execute_query
|
|
from tenancy import school_singleton, met_school, huidige_school, in_school
|
|
|
|
VERVERS_UREN = 24
|
|
PARALLEL = 8
|
|
|
|
|
|
class DelegatieOverzicht:
|
|
def __new__(cls):
|
|
def init(inst):
|
|
inst._lock = threading.Lock()
|
|
inst.status = {'bezig': False, 'voortgang': 0, 'totaal': 0, 'fout': None, 'resultaat': None}
|
|
return school_singleton(cls, init)
|
|
|
|
@staticmethod
|
|
def ensure_table():
|
|
execute_query("""
|
|
CREATE TABLE IF NOT EXISTS gmail_delegaties (
|
|
Mailbox VARCHAR(255) NOT NULL,
|
|
Mailbox_naam VARCHAR(255) NULL,
|
|
Gedelegeerde VARCHAR(255) NOT NULL,
|
|
Gedelegeerde_naam VARCHAR(255) NULL,
|
|
Status VARCHAR(20) NULL,
|
|
Opgehaald DATETIME NOT NULL,
|
|
PRIMARY KEY (Mailbox, Gedelegeerde),
|
|
KEY idx_gmail_deleg_gedelegeerde (Gedelegeerde)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""", commit=True)
|
|
execute_query("CREATE TABLE IF NOT EXISTS gmail_delegaties_scan (ID TINYINT NOT NULL PRIMARY KEY, "
|
|
"Opgehaald DATETIME NULL, Mailboxen INT NULL, Fouten INT NULL) ENGINE=InnoDB", commit=True)
|
|
|
|
# ---------------- opvragen ----------------
|
|
def alle(self):
|
|
return execute_query("SELECT * FROM gmail_delegaties ORDER BY Mailbox, Gedelegeerde") or []
|
|
|
|
def laatst_opgehaald(self):
|
|
rij = execute_query("SELECT Opgehaald, Mailboxen, Fouten FROM gmail_delegaties_scan WHERE ID = 1")
|
|
return rij[0] if rij else None
|
|
|
|
def verouderd(self):
|
|
laatst = self.laatst_opgehaald()
|
|
return not laatst or not laatst['Opgehaald'] or \
|
|
(datetime.now() - laatst['Opgehaald']).total_seconds() > VERVERS_UREN * 3600
|
|
|
|
# ---------------- één mailbox bijwerken (na toevoegen/intrekken/openen in PIVOT) ----------------
|
|
def bewaar_mailbox(self, mailbox, naam, delegates, namen=None):
|
|
"""Vervangt de rijen van één mailbox door de opgegeven gedelegeerden [{'email','status'}]."""
|
|
try:
|
|
self.ensure_table()
|
|
# Namen die we al kennen (uit de volledige ophaling) behouden
|
|
bekend = {}
|
|
for r in execute_query("SELECT Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam FROM gmail_delegaties") or []:
|
|
if r['Mailbox_naam']:
|
|
bekend[r['Mailbox'].lower()] = r['Mailbox_naam']
|
|
if r['Gedelegeerde_naam']:
|
|
bekend[r['Gedelegeerde'].lower()] = r['Gedelegeerde_naam']
|
|
namen = {**bekend, **(namen or {})}
|
|
execute_query("DELETE FROM gmail_delegaties WHERE Mailbox = %s", (mailbox,), commit=True)
|
|
for d in delegates:
|
|
execute_query("INSERT INTO gmail_delegaties (Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam, "
|
|
"Status, Opgehaald) VALUES (%s, %s, %s, %s, %s, NOW())",
|
|
(mailbox, naam, d['email'], namen.get(d['email'].lower()) or d.get('naam'),
|
|
d.get('status')), commit=True)
|
|
except Exception as e:
|
|
sys.stderr.write(f"Delegaties: mailbox {mailbox} niet bewaard: {e}\n")
|
|
|
|
# ---------------- alles ophalen uit Google ----------------
|
|
def _gebruikers(self, directory):
|
|
uit, token = [], None
|
|
while True:
|
|
resp = directory.users().list(customer='my_customer', maxResults=500, pageToken=token,
|
|
fields='nextPageToken,users(primaryEmail,name/fullName,suspended)').execute()
|
|
for u in resp.get('users', []):
|
|
uit.append({'email': u['primaryEmail'].lower(), 'naam': (u.get('name') or {}).get('fullName', ''),
|
|
'geschorst': bool(u.get('suspended'))})
|
|
token = resp.get('nextPageToken')
|
|
if not token:
|
|
return uit
|
|
|
|
def sync(self):
|
|
if not self._lock.acquire(blocking=False):
|
|
return False, "Er loopt al een ophaling."
|
|
try:
|
|
from services.gmail_delegation_service import GmailDelegationService, DelegationError
|
|
self.status.update(bezig=True, voortgang=0, totaal=0, fout=None, resultaat=None)
|
|
self.ensure_table()
|
|
svc = GmailDelegationService()
|
|
gebruikers = self._gebruikers(svc._directory_api())
|
|
namen = {u['email']: u['naam'] for u in gebruikers}
|
|
actief = [u for u in gebruikers if not u['geschorst']]
|
|
self.status['totaal'] = len(actief)
|
|
school = huidige_school()
|
|
fouten, rechtenfout, gelukt = [], [], []
|
|
|
|
def een(u):
|
|
# Krijgt Google telkens dezelfde rechtenfout, dan niet alle gebruikers blijven proberen
|
|
if len(rechtenfout) >= 3 and not gelukt:
|
|
self.status['voortgang'] += 1
|
|
return u, None
|
|
with in_school(school):
|
|
try:
|
|
delegates = svc.list_delegates(u['email'])
|
|
gelukt.append(1)
|
|
return u, delegates
|
|
except DelegationError as e:
|
|
tekst = str(e)
|
|
if 'serviceaccount mist een Gmail-recht' in tekst or 'Gmail API staat nog niet aan' in tekst:
|
|
rechtenfout.append(tekst)
|
|
fouten.append(u['email'])
|
|
return u, None
|
|
except Exception:
|
|
fouten.append(u['email'])
|
|
return u, None
|
|
finally:
|
|
self.status['voortgang'] += 1
|
|
|
|
resultaten = []
|
|
with ThreadPoolExecutor(max_workers=PARALLEL) as pool:
|
|
for u, delegates in pool.map(een, actief):
|
|
if delegates:
|
|
resultaten.append((u, delegates))
|
|
if rechtenfout and not gelukt:
|
|
raise RuntimeError(rechtenfout[0])
|
|
|
|
nu = datetime.now()
|
|
execute_query("DELETE FROM gmail_delegaties", commit=True)
|
|
aantal = 0
|
|
for u, delegates in resultaten:
|
|
for d in delegates:
|
|
execute_query("INSERT INTO gmail_delegaties (Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam, "
|
|
"Status, Opgehaald) VALUES (%s, %s, %s, %s, %s, %s)",
|
|
(u['email'], u['naam'], d['email'], namen.get(d['email'].lower()), d['status'], nu),
|
|
commit=True)
|
|
aantal += 1
|
|
execute_query("INSERT INTO gmail_delegaties_scan (ID, Opgehaald, Mailboxen, Fouten) VALUES (1, %s, %s, %s) "
|
|
"ON DUPLICATE KEY UPDATE Opgehaald = VALUES(Opgehaald), Mailboxen = VALUES(Mailboxen), "
|
|
"Fouten = VALUES(Fouten)", (nu, len(actief), len(fouten)), commit=True)
|
|
msg = f"{len(actief)} mailboxen nagekeken: {aantal} machtiging(en) bij {len(resultaten)} mailbox(en)."
|
|
if fouten:
|
|
msg += f" {len(fouten)} mailbox(en) konden niet bevraagd worden (bv. zonder Gmail-licentie)."
|
|
self.status['resultaat'] = msg
|
|
return True, msg
|
|
except Exception as e:
|
|
self.status['fout'] = str(e)
|
|
return False, f"Ophalen mislukt: {e}"
|
|
finally:
|
|
self.status['bezig'] = False
|
|
self._lock.release()
|
|
|
|
def sync_op_achtergrond(self):
|
|
if self.status['bezig']:
|
|
return False
|
|
self.status.update(bezig=True, voortgang=0, totaal=0, fout=None)
|
|
threading.Thread(target=met_school(self.sync), daemon=True).start()
|
|
return True
|