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
@@ -195,6 +195,58 @@ export const catalog: Catalog = {
"Your organisation has reached the number of mailing lists it is allowed.": "Ваша організація досягла дозволеної кількості списків розсилки.",
"This mailing list no longer exists. Someone may have deleted it.": "Цього списку розсилки більше немає. Можливо, його хтось видалив.",
"The server did not say whether the list was created.": "Сервер не повідомив, чи створено список.",
"Roles": "Ролі",
"users": "користувачам",
"groups": "групам",
"tenant administrators": "адміністраторам орендарів",
"administrators": "адміністраторам",
"A role needs a name.": "Ролі потрібна назва.",
"Created {name}": "Створено роль {name}",
"New role": "Нова роль",
"This role carries permissions yours doesn't, so you can view it but not change it.": "Ця роль має дозволи, яких немає у вашої, тому її можна переглядати, але не змінювати.",
"Your role lets you view roles but not change them.": "Ваша роль дозволяє переглядати ролі, але не змінювати їх.",
"Stalwart gives this role by default to {kinds}. A change here reaches everyone who has it that way.": "Stalwart за замовчуванням надає цю роль {kinds}. Зміна тут стосується всіх, хто отримав її так.",
"Builds on": "Базується на",
"Permissions": "Дозволи",
"Stalwart gives this role by default, so it can't be deleted. Change the defaults in Stalwart's own administration first.": "Stalwart надає цю роль за замовчуванням, тому її не можна видалити. Спершу змініть значення за замовчуванням у власній панелі адміністрування Stalwart.",
"This role carries permissions yours doesn't.": "Ця роль має дозволи, яких немає у вашої.",
"Create role": "Створити роль",
"builds on this one": "базується на цій",
"has permissions yours doesn't": "має дозволи, яких немає у вашої",
"No other roles": "Інших ролей немає",
"A role has every permission of the roles it builds on, apart from any it or they deny.": "Роль має всі дозволи ролей, на яких базується, окрім тих, які забороняє вона чи вони.",
"Search permissions": "Пошук дозволів",
"All permissions": "Усі дозволи",
"Granted": "Надані",
"Set on this role": "Задані в цій ролі",
"No permissions match": "Немає відповідних дозволів",
"{granted} of {total}": "{granted} з {total}",
"Denied by {role}": "Заборонено роллю {role}",
"Granted by {role}": "Надано роллю {role}",
"Inherit": "Успадковувати",
"Not set": "Не задано",
"Allow": "Дозволити",
"Deny": "Заборонити",
"A denial wins over anything allowed, here or on a role this one builds on. You can only allow permissions you hold yourself.": "Заборона переважає будь-який дозвіл — тут чи в ролі, на якій базується ця. Дозволяти можна лише те, що маєте ви самі.",
"Accounts, groups and other roles that use it must be moved off it first.": "Спершу приберіть цю роль в облікових записів, груп та інших ролей, які її використовують.",
"Delete role…": "Видалити роль…",
"Deleted {name}": "Роль {name} видалено",
"Still used by {things}. Move them to another role first.": "Ще використовується: {things}. Спершу призначте їм іншу роль.",
"Delete role": "Видалити роль",
"It can't be undone.": "Це не можна скасувати.",
"Type {name} to confirm": "Введіть {name} для підтвердження",
"Stalwart's list of permissions could not be loaded, so permissions can't be changed here. ({reason})": "Не вдалося завантажити список дозволів Stalwart, тому змінити дозволи тут не можна. ({reason})",
"Named sets of permissions, given to accounts, groups and tenants.": "Іменовані набори дозволів для облікових записів, груп та орендарів.",
"Search roles": "Пошук ролей",
"No roles match": "Немає відповідних ролей",
"No roles yet": "Ролей поки немає",
"Open {name}": "Відкрити {name}",
"Default for {kinds}": "За замовчуванням: {kinds}",
"You can't give a role permissions your own role doesn't have.": "Не можна надати ролі дозволи, яких немає у вашої власної ролі.",
"Your organisation has reached the number of roles it is allowed.": "Ваша організація досягла дозволеної кількості ролей.",
"This role no longer exists. Someone may have deleted it.": "Цієї ролі більше немає. Можливо, її хтось видалив.",
"the default roles": "налаштування ролей за замовчуванням",
"The server did not say whether the role was created.": "Сервер не повідомив, чи створено роль.",
"User": "Користувач",
"Administrator": "Адміністратор",
"Custom role": "Власна роль",
@@ -1591,6 +1643,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: "Спочатку {n} учасник прибирається з групи й утрачає те, чим із нею поділилися. Пошта групи видаляється у фоновому режимі, і це не можна скасувати.", few: "Спочатку {n} учасники прибираються з групи й утрачають те, чим із нею поділилися. Пошта групи видаляється у фоновому режимі, і це не можна скасувати.", many: "Спочатку {n} учасників прибирають із групи, і вони втрачають те, чим із нею поділилися. Пошта групи видаляється у фоновому режимі, і це не можна скасувати.", other: "Спочатку {n} учасника прибирають із групи, і вони втрачають те, чим із нею поділилися. Пошта групи видаляється у фоновому режимі, і це не можна скасувати." },
"{n} mailing lists": { one: "{n} список розсилки", few: "{n} списки розсилки", many: "{n} списків розсилки", other: "{n} списку розсилки" },
"{n} recipients": { one: "{n} одержувач", few: "{n} одержувачі", many: "{n} одержувачів", other: "{n} одержувача" },
"Grants {n} permissions": { one: "Надає {n} дозвіл", few: "Надає {n} дозволи", many: "Надає {n} дозволів", other: "Надає {n} дозволу" },
"{n} roles": { one: "{n} роль", few: "{n} ролі", many: "{n} ролей", other: "{n} ролі" },
"{n} DKIM keys": { one: "{n} ключ DKIM", few: "{n} ключі DKIM", many: "{n} ключів DKIM", other: "{n} ключа DKIM" },
"{n} other items": { one: "{n} інший об'єкт", few: "{n} інші об'єкти", many: "{n} інших об'єктів", other: "{n} іншого об'єкта" },
// ── Administration ────────────────────────────────────────────────