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
@@ -201,6 +201,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": "Особая роль",
@@ -1597,6 +1649,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 ────────────────────────────────────────────────