19. Die REST-API von GNU Mailman 3¶
pepsi-httpd bietet die REST-API von GNU Mailman 3 unter /3.0/ und /3.1/ an. Das ist keine API eigenen Entwurfs: es ist die Schnittstelle eines anderen, nachgebaut, damit Software, die für GNU Mailman 3 geschrieben wurde, unverändert gegen Pepsi läuft. mailmanclient, Postorius und HyperKitty sind die Clients, für die sie existiert.
Implementiert ist Version 3.3.10. Diese Genauigkeit zählt: „Mailman 3“ ist kein Vertrag, denn der Attributsatz verschiebt sich zwischen Punktversionen, während der Pfad /3.1/ es nicht tut. Die Schnittstelle ist beim GNU-Mailman-Projekt unter https://docs.mailman3.org/ dokumentiert, und jene Dokumentation – nicht dieses Kapitel – ist die Spezifikation. Was folgt, ist das, was ein Betreiber braucht, um sie zu betreiben, dazu die drei Stellen, an denen Pepsi etwas Wahres antworten muss, anstatt etwas zu kopieren, das es nicht hat.
Bemerkung
Namensnennung. Der Ressourcenbaum, die Attributnamen, die Aufzählungswerte, die Konventionen auf der Leitung und die Fehlermeldungen sind die von GNU Mailman, urheberrechtlich geschützt durch die Free Software Foundation, und werden hier wiedergegeben, um damit kompatibel zu sein. Siehe vendor/PEPSI-VENDORING.md. Pepsi ist nicht GNU Mailman und wird vom GNU-Mailman-Projekt nicht unterstützt.
Dieses Kapitel behandelt die Schnittstelle. Was die Ressourcen bedeuten – eine Liste, ein Mitglied, eine Moderationsentscheidung, eine Sammelnachricht – steht in Mailinglisten, dessen Wie das Subsystem aufgebaut ist zeigt, wo diese API im Verhältnis zum /api/v1 des Betreibers und zu den öffentlichen Mitgliederseiten steht; die Ressourcen des Archivs sind Archive. Den Listener einzuschalten und die dafür nötigen Zugangsdaten behandelt Installation; die Browseroberflächen, die Clients desselben Modells sind, sind Die Verwaltungskonsole; und was Pepsi überhaupt implementiert, steht in Unterstützte Funktionen.
19.1. In diesem Baum gibt es zwei APIs¶
Ein Betreiber wird beiden begegnen, und sie haben nichts gemeinsam außer dem Server:
/api/v1Pepsis eigene administrative API (Die administrative API). Ihr Publikum ist der Betreiber: die Warteschlange, der Schlüsselspeicher, die Protokolle, die Konfiguration. Sie hat Konten, Scopes, Sitzungen, Bearer-Token und CSRF, ihre Sammlungen sind
{items, total, limit, offset}und ihre Fehler{code, hint, detail}./3.0/und/3.1/Diese hier. Ihr Publikum ist Mailinglisten-Software. Sie hat ein einziges gemeinsames Passwort und keine Scopes, ihre Sammlungen sind
{start, total_size, entries}und ihre Fehler sind die von Falcon:{title, description}.
Sie sind absichtlich nicht vereinheitlicht. Der ganze Wert dieser einen liegt darin, dass ihre Formen nicht die unseren zum Verbessern sind.
19.2. Wo sie angeboten wird¶
Nur auf einem Listener mit dem Flag LIST_API = yes. Auf jedem anderen Listener antworten die Routen /3.0/ und /3.1/ mit einem einfachen 404, Byte für Byte das, was jeder unbekannte Pfad erhält, sodass ein öffentlicher Listener nicht danach abgetastet werden kann, ob die API in dieser Installation aktiviert ist.
Hier gilt dieselbe Regel wie für die administrative API, und aus demselben Grund – die Zugangsdaten sind ein einziges statisches Passwort, das daher kein Klartextnetz überqueren darf:
ein TLS-Listener ist immer erlaubt;
ein Unix-Socket-Listener ist immer erlaubt (er verlässt den Rechner nie);
ein TCP-Listener im Klartext ist nur auf einer Loopback-Adresse erlaubt;
ein von systemd aktivierter Socket im Klartext wird verweigert, weil der Server nicht sehen kann, wo systemd ihn gebunden hat.
Ein Listener, der das Flag verlangt, aber anderswo gebunden ist, bietet die API nicht an, und pepsi-httpd sagt das beim Start laut, anstatt still zu versagen.
[pepsi-httpd-listener-mailman]
# What every existing mailman.cfg-shaped client already points at.
BIND_TO = 127.0.0.1:8001
MODE = plain
LIST_API = yes
[pepsi-list]
API_USER = restadmin
API_PASS = a-long-random-string
Warnung
API_USER und API_PASS sind ein gemeinsames Zugangsdatenpaar für den ganzen Server. Wer es besitzt, kann jede Liste, jedes Abonnement und jeden Benutzerdatensatz lesen und ändern. Das ist das Modell von upstream und keine Vereinfachung davon; halten Sie den Listener auf Loopback oder hinter TLS, und verwenden Sie das Passwort nirgends sonst. pepsi-httpd verweigert den Start, wenn ein Listener die API verlangt und die Zugangsdaten unvollständig sind: eine API, deren gesamtes Autorisierungsmodell dieses Passwort ist, ohne eines anzubieten, ist kein eingeschränkter Betriebsmodus.
19.3. Authentifizierung¶
HTTP Basic gegen dieses eine Paar, in konstanter Zeit verglichen. Es gibt keine Sitzungen, keine CSRF-Token, keine benutzerbezogenen Konten und keine Tokenausgabe: ein Client sendet Authorization: Basic ... bei jeder Anfrage. Ein Fehlschlag ist 401 mit
WWW-Authenticate: Basic realm="mailman3-rest",charset="utf-8"
und diese Realm-Zeichenkette ist die von upstream, weil ein Client sie anzeigen kann.
Pepsi fügt einen Ratenbegrenzer hinzu, den upstream überhaupt nicht hat. Es ist eine Obermenge und kann einen korrekten Client nicht brechen, aber eine Kompatibilitätssuite feuert Hunderte Anfragen in Sekunden ab, daher ist der Standard absichtlich hoch – 3000 Anfragen pro Minute und Quelladresse, einstellbar mit [pepsi-list] API_RATE_LIMIT. Ein 429 von dieser API ist Pepsis, nicht Mailmans.
19.4. Die beiden Versionen unterscheiden sich in vier Punkten¶
Nicht in einem, was der übliche Irrtum ist:
die sechs
*_uri-Vorlagenattribute sind in/3.0/Listenattribute und fehlen in/3.1/, das sie stattdessen über/uriserreicht;/templates/...existiert nur in/3.0/;/uris/...,/lists/<id>/uris,/domains/<host>/urisund/pluginsexistieren nur in/3.1/. Jedes davon ist in der anderen Version ein404;Identifikatoren werden unterschiedlich serialisiert.
/3.0/gibt eine UUID alsuuid.intaus, eine Dezimalzahl mit bis zu neununddreißig Stellen;/3.1/gibtuuid.hexaus, eine Zeichenkette. Das betrifftmember_id,user_idund jeden daraus gebautenself_link, und es ist der Grund, weshalb/3.1/überhaupt existiert – die Ganzzahlform ist nicht in jeder JavaScript-Laufzeitumgebung darstellbar;jeder
self_linkund jedesLocationträgt sein eigenes Versionspräfix.
19.5. Vorlagen-URIs¶
Eine Vorlagenüberschreibung, die über eine der beiden Versionen geschrieben wird — ein *_uri-Attribut in /3.0/ oder /uris in /3.1/ —, ist eine URI, die der Server abruft, sodass wer immer die REST-Zugangsdaten hält, bestimmt, was der Server liest. Pepsi akzeptiert mailman: (den eingebauten Text) und http(s):, abgerufen unter den Grenzen in [pepsi-list] TEMPLATE_FETCH_*, und antwortet auf eine file:-URI mit 400, die upstream von der eigenen Platte lesen würde. Ein file:-Datensatz, der dennoch in der Tabelle steht (von Hand geschrieben), wird nie gelesen: Die Benachrichtigung wird so gerendert, als fehlte der Datensatz.
19.6. Drei ehrliche Entsprechungen¶
Drei Ressourcen beschreiben Dinge, die GNU Mailman hat und Pepsi nicht. Keine davon ist vorgetäuscht.
/queuesDie Queues von upstream sind Verzeichnisse voller
.pck-Dateien. Pepsi hat eine Tabelle: eine Nachricht in Bearbeitung ist ein Datensatz inpepsi.workqueuean irgendeiner Stage. Diese Ressource antwortet daher mit den echten Zahlen –insind alle Nachrichten in Bearbeitung,retrydie für einen späteren Versuch pausierten,badundshuntdie beiden Endzustände, die der Dispatcher nie wieder einreiht –, wobei dieworkqueue_idfür einenfilebasesteht und der Tabellenname dort steht, wo ein Verzeichnis stünde.POST /queues/<name>speist eine Nachricht an der Posting-Stage der Liste ein, undDELETE /queues/<name>/<id>entfernt einen Datensatz. pepsi-queue(1) bleibt das eigentliche Werkzeug./pluginsImmer eine leere Sammlung. Pepsi hat keine Plugin-Schnittstelle und keine einsteckbaren Archivierer. In
/3.0/gibt es diese Ressource nicht./reservedDer eigene Testhaken von upstream, den upstream selbst als nicht Teil der stabilen API kennzeichnet.
GET /reserved/resetleert jede Listen- und Archivtabelle, es existiert daher nur auf einem Listener, der sich mitLIST_API_TESTING = yesausdrücklich dafür entschieden hat; überall sonst ist es ein404, das von einem unbekannten Pfad nicht zu unterscheiden ist, und ein Test prüft diese Voreinstellung. Es ist da, weil die Kompatibilitätssuiten den Zustand zwischen Testklassen im eigenen Prozess zurücksetzen, was ein Server in einem anderen Prozess nicht anbieten kann.DELETE /reserved/uids/orphansist eine erfolgreiche Leeroperation: Identifikatoren sind hier UUIDs der Version 4, und es gibt keine Waisentabelle auszudünnen.
19.7. Was über die Zugangsdaten hinaus konfiguriert werden muss¶
[pepsi-list] RELEASE_STAGEDer Abschnitt
[stage-<name>], in dempepsi-stage-list-postläuft. Ein Moderator, der eine zurückgehaltene Nachricht annimmt, undPOST /queues/<name>setzen eine Nachricht beide dort wieder in die Pipeline, und nur die Installation weiß, wie dieser Abschnitt heißt. Ohne ihn antworten diese beiden Vorgänge mit400und sagen es – was besser ist, als204zu antworten und die Nachricht fallen zu lassen, denn ein Moderator, der eine Nachricht annimmt und zusieht, wie sie nie ankommt, kann das nicht von einem Zustellfehler unterscheiden.[pepsi-list] DEFAULT_LANGUAGEDie Unterkante der Präferenzkette, gemeldet von
GET /<api>/system/preferences. Standard isten.
19.8. Ein durchgearbeitetes Beispiel¶
Halten Sie die Zugangsdaten aus der Kommandozeile heraus – auf einem gemeinsam genutzten Rechner stehen sie in jeder Prozessliste und in der Shell-Historie –, indem Sie sie in eine Datei schreiben und curl daraus lesen lassen:
$ cat > ~/.mailman-netrc <<'EOF'
machine localhost login restadmin password a-long-random-string
EOF
$ chmod 600 ~/.mailman-netrc
$ alias mmcurl='curl -sS --netrc-file ~/.mailman-netrc'
$ mmcurl http://localhost:8001/3.1/system/versions
{"mailman_version":"GNU Mailman 3.3.10 (Tom Sawyer; implemented by Pepsi …)",
"python_version":"…","api_version":"3.1",
"self_link":"http://localhost:8001/3.1/system/versions",
"http_etag":"\"…\""}
$ mmcurl -X POST --data 'mail_host=lists.example.com' \
http://localhost:8001/3.1/domains
$ mmcurl -X POST --data 'fqdn_listname=devel@lists.example.com' \
http://localhost:8001/3.1/lists
$ mmcurl 'http://localhost:8001/3.1/lists?count=10&page=1'
mailman_version verdient einen zweiten Blick. Es muss mit der Zeichenkette beginnen, auf die ein Client prüft, und es muss wahr sein; es nennt daher zuerst die Version der Schnittstelle und sagt in der Klammer, die upstream mit einem Codenamen füllt, was tatsächlich läuft. Pepsi behauptet nicht, GNU Mailman zu sein.
POST /lists/<id>/digest: bump erhöht den Band, und send und periodic laufen beide über denselben Versender, den auch die Kommandozeile benutzt, sodass eine von einem Client ausgelöste Ausgabe und eine von einem Timer ausgelöste derselbe Codeweg sind und dieselbe Numerierung tragen.
19.9. Was nicht implementiert ist¶
Eine zurückgewiesene zurückgehaltene Nachricht oder Anfrage wird verworfen, ohne die Zurückweisungsbenachrichtigung, die upstream sendet. Annehmen, Verwerfen und Zurückstellen sind vollständig.
19.10. Sie testen¶
tests/22-list-api-test.sh führt die eigenen Testsuiten von upstream gegen ein installiertes Pepsi aus: mailmanclient 3.3.5 (344 Doctest-Anweisungen und 36 Testfunktionen) und Postorius 1.3.13 (30 Dateien, 248 Testfunktionen), jeweils nur mit ausgetauschter Vorrichtung zum Starten des Servers. Die Ersatzstücke liegen in contrib/compat/ und sind neben den festgeschriebenen Anforderungen versioniert, sodass „unveränderte Upstream-Tests“ für alles außer der Vorrichtung wörtlich wahr bleibt. Siehe Testsuite.