Add Roles to Administration, with Stalwart's permissions in every language

A role is a named set of permissions given to accounts, groups and
tenants. It gets its own section under a new Access heading: every role
listed with the permissions it grants once its bases are followed, and a
panel to create, edit and delete one.

A role builds on others and has everything they grant; a denial anywhere in
the tree wins, which is how Stalwart resolves it (permissions.rs unions
enabled and disabled across the tree, then subtracts). The picker is
Stalwart's own list of permissions, under its headings, searchable and
filterable to what is granted or set here. Each permission is not set,
allowed or denied, and one that is inherited says which role it comes from.
Only permissions the viewer holds can be allowed, because Stalwart refuses
the rest, and a role carrying anything the viewer lacks opens read-only with
no delete, because Stalwart checks a grant but not a delete. Saving sends a
pointer for each permission and base role that changed.

The roles Stalwart hands out by default, read from x:Authentication, say so
before they are changed and cannot be deleted here; a role still in use is
kept by the server, and the refusal names what uses it.

The permission list is Stalwart's schema. A new route, GET
/api/admin/permissions, fetches /api/schema as the signed-in account and
returns only names and labels, behind the same two gates as the registry
methods and held in memory for an hour. Its labels are English only, so
every one of the 661 has a translation in each of the eight other
languages, in its own file keyed by permission name and loaded only when
Roles opens. A permission a later Stalwart adds shows its English label. A
test holds every language to the 0.16.22 snapshot: nothing missing, nothing
stale.

The mock answers x:Role/set with the grant check, loops and in-use
refusals, reads the defaults from x:Authentication, and serves the schema
gzipped as the real one is.

Fifty-two new strings and two plurals in all nine catalogues, and 661
permission labels with 59 headings in each of the eight translations.
This commit is contained in:
2026-09-15 09:13:05 -07:00
parent 627422d794
commit a00d07b430
42 changed files with 11021 additions and 12 deletions
+54
View File
@@ -207,6 +207,58 @@ export const catalog: Catalog = {
"Your organisation has reached the number of mailing lists it is allowed.": "Ihre Organisation hat die erlaubte Anzahl an Mailinglisten erreicht.",
"This mailing list no longer exists. Someone may have deleted it.": "Diese Mailingliste existiert nicht mehr. Jemand hat sie möglicherweise gelöscht.",
"The server did not say whether the list was created.": "Der Server hat nicht mitgeteilt, ob die Liste angelegt wurde.",
"Roles": "Rollen",
"users": "Benutzern",
"groups": "Gruppen",
"tenant administrators": "Mandantenadministratoren",
"administrators": "Administratoren",
"A role needs a name.": "Eine Rolle braucht einen Namen.",
"Created {name}": "{name} angelegt",
"New role": "Neue Rolle",
"This role carries permissions yours doesn't, so you can view it but not change it.": "Diese Rolle hat Berechtigungen, die Ihre nicht hat. Sie können sie ansehen, aber nicht ändern.",
"Your role lets you view roles but not change them.": "Ihre Rolle erlaubt es, Rollen anzusehen, aber nicht zu ändern.",
"Stalwart gives this role by default to {kinds}. A change here reaches everyone who has it that way.": "Stalwart vergibt diese Rolle standardmäßig an {kinds}. Eine Änderung betrifft alle, die sie auf diesem Weg haben.",
"Builds on": "Baut auf",
"Permissions": "Berechtigungen",
"Stalwart gives this role by default, so it can't be deleted. Change the defaults in Stalwart's own administration first.": "Stalwart vergibt diese Rolle standardmäßig, daher kann sie nicht gelöscht werden. Ändern Sie zuerst die Standardwerte in der Verwaltung von Stalwart selbst.",
"This role carries permissions yours doesn't.": "Diese Rolle hat Berechtigungen, die Ihre nicht hat.",
"Create role": "Rolle anlegen",
"builds on this one": "baut auf dieser auf",
"has permissions yours doesn't": "hat Berechtigungen, die Ihre nicht hat",
"No other roles": "Keine anderen Rollen",
"A role has every permission of the roles it builds on, apart from any it or they deny.": "Eine Rolle hat alle Berechtigungen der Rollen, auf denen sie aufbaut, außer denen, die sie oder jene verweigern.",
"Search permissions": "Berechtigungen durchsuchen",
"All permissions": "Alle Berechtigungen",
"Granted": "Gewährt",
"Set on this role": "Auf dieser Rolle gesetzt",
"No permissions match": "Keine passenden Berechtigungen",
"{granted} of {total}": "{granted} von {total}",
"Denied by {role}": "Verweigert durch {role}",
"Granted by {role}": "Gewährt durch {role}",
"Inherit": "Erben",
"Not set": "Nicht gesetzt",
"Allow": "Erlauben",
"Deny": "Verweigern",
"A denial wins over anything allowed, here or on a role this one builds on. You can only allow permissions you hold yourself.": "Eine Verweigerung hat Vorrang vor jeder Erlaubnis, hier oder auf einer Rolle, auf der diese aufbaut. Sie können nur Berechtigungen erlauben, die Sie selbst haben.",
"Accounts, groups and other roles that use it must be moved off it first.": "Konten, Gruppen und andere Rollen, die sie nutzen, müssen zuerst davon gelöst werden.",
"Delete role…": "Rolle löschen…",
"Deleted {name}": "{name} gelöscht",
"Still used by {things}. Move them to another role first.": "Noch verwendet von {things}. Weisen Sie diesen zuerst eine andere Rolle zu.",
"Delete role": "Rolle löschen",
"It can't be undone.": "Das lässt sich nicht rückgängig machen.",
"Type {name} to confirm": "Geben Sie {name} zur Bestätigung ein",
"Stalwart's list of permissions could not be loaded, so permissions can't be changed here. ({reason})": "Die Berechtigungsliste von Stalwart konnte nicht geladen werden, daher lassen sich Berechtigungen hier nicht ändern. ({reason})",
"Named sets of permissions, given to accounts, groups and tenants.": "Benannte Sätze von Berechtigungen, vergeben an Konten, Gruppen und Mandanten.",
"Search roles": "Rollen durchsuchen",
"No roles match": "Keine passenden Rollen",
"No roles yet": "Noch keine Rollen",
"Open {name}": "{name} öffnen",
"Default for {kinds}": "Standard bei {kinds}",
"You can't give a role permissions your own role doesn't have.": "Sie können einer Rolle keine Berechtigungen geben, die Ihre eigene Rolle nicht hat.",
"Your organisation has reached the number of roles it is allowed.": "Ihre Organisation hat die erlaubte Anzahl an Rollen erreicht.",
"This role no longer exists. Someone may have deleted it.": "Diese Rolle existiert nicht mehr. Jemand hat sie möglicherweise gelöscht.",
"the default roles": "den Standardrollen",
"The server did not say whether the role was created.": "Der Server hat nicht mitgeteilt, ob die Rolle angelegt wurde.",
"User": "Benutzer",
"Administrator": "Administrator",
"Custom role": "Eigene Rolle",
@@ -1622,6 +1674,8 @@ export const catalog: Catalog = {
"Its {n} members are taken out of the group first, and lose what was shared with it. The group's own mail is removed in the background, and it can't be undone.": { one: "Sein {n} Mitglied wird zuerst aus der Gruppe entfernt und verliert, was mit ihr geteilt wurde. Die E-Mails der Gruppe werden im Hintergrund entfernt, und das lässt sich nicht rückgängig machen.", other: "Ihre {n} Mitglieder werden zuerst aus der Gruppe entfernt und verlieren, was mit ihr geteilt wurde. Die E-Mails der Gruppe werden im Hintergrund entfernt, und das lässt sich nicht rückgängig machen." },
"{n} mailing lists": { one: "{n} Mailingliste", other: "{n} Mailinglisten" },
"{n} recipients": { one: "{n} Empfänger", other: "{n} Empfänger" },
"Grants {n} permissions": { one: "Gewährt {n} Berechtigung", other: "Gewährt {n} Berechtigungen" },
"{n} roles": { one: "{n} Rolle", other: "{n} Rollen" },
"{n} DKIM keys": { one: "{n} DKIM-Schlüssel", other: "{n} DKIM-Schlüssel" },
"{n} other items": { one: "{n} weiteres Objekt", other: "{n} weitere Objekte" },
// ── Administration ────────────────────────────────────────────────