Роли
`roles.list`, `get`, `create`, `update`, `delete` и `listPermissions`.
Все методы
const roles = await openemail.roles.list()const role = await openemail.roles.get('role_…') const support = await openemail.roles.create({ name: 'Support', description: 'Answers the shared inboxes and nothing else.', permissions: ['emails:send', 'threads:write', 'labels:write'],}) console.log(support.permissions) await openemail.roles.update(support.id, { permissions: [...support.permissions, 'templates:read'],}) await openemail.roles.delete(support.id, { reassignTo: 'role_…' }) const vocabulary = await openemail.roles.listPermissions()support.permissions содержит шесть записей, а не три: emails:send тянет за собой emails:read, threads:write — threads:read, а labels:write — labels:read. Прочитайте список обратно, а не предполагайте его состав.
Роль говорит, что человеку можно ДЕЛАТЬ. С какими АДРЕСАМИ он может это делать — вторая ось, и она живёт в openemail.members. Смотрите там grantAddress и revokeAddress. «Может отправлять почту» и «может отправлять от имени invoices@» — разные утверждения, и рабочее пространство, нанявшее второго специалиста поддержки, меняет второе, не трогая первое.
Ветвитесь по editable и deletable, а не по имени в builtin. Оба равны false только у владельца, чей список прав — это «все права, включая те, что придумают в следующем году», и он вычисляется, а не хранится; все остальные роли отвечают true на оба, включая пять, которыми засевается рабочее пространство. Переименованная кем-то роль по-прежнему отвечает на оба правильно, а её имя уже ни о чём не говорит.
update ЗАМЕНЯЕТ список прав. Вызова для выдачи одного права нет, поэтому прочитайте роль, измените нужную запись и отправьте обратно весь список. Отправка одного права оставит роль ровно с ним и с тем, что из него следует.
delete требует reassignTo, как только роль кем-то занята, и параметр передаётся в строке запроса, потому что тело в DELETE отбрасывают несколько сред выполнения и ряд прокси. Результат сообщает reassigned и keysReassigned по отдельности, чтобы скрипт мог записать в лог то, что он сделал, а не то, о чём просил.
listPermissions() — это GET /roles/permissions, фиксированный путь ровно там, где стоял бы идентификатор роли. Клиент зашивает его, а не пропускает строку через get, поэтому запрос роли, которая и правда называется «permissions», остаётся запросом роли и получает 404 — честный ответ на то, что было набрано. scope: false помечает записи, которыми не может обладать ни один ключ.
Роль — это потолок для ключа
Ключ, выпущенный под роль, может делать то, что даёт ПЕРЕСЕЧЕНИЕ его собственных областей с правами этой роли; оно вычисляется для каждого запроса на границе. Поэтому сужение роли отзывает права её ключей на лету, без всякой ротации, а у ключа без роли потолка нет вовсе — то есть null в роли означает самое широкое состояние ключа, а не самое узкое.
Именно поэтому roles.delete настаивает на том, чтобы было куда перенести ключи. Осиротив их, вы бы полностью сняли потолок, молча повысив в правах каждую учётную запись, которую роль ограничивала.
GET /keys/self и GET /ping сообщают roleId и grantedScopes рядом с действующими scopes — так и отвечают на «у моего ключа есть emails:send, а я получаю insufficient_scope»: всё, что есть в grantedScopes и отсутствует в scopes, забрала роль. openemail.me.get() и openemail.me.ping() возвращают и то, и другое, с типами.
Параметры
namestringобязательно- Как рабочее пространство называет роль: от 1 до 48 символов, пробелы по краям обрезаются перед сохранением. Имена уникальны в пределах рабочего пространства без учёта регистра, поэтому второй «Support» отклоняется с `role_name_taken` (409), а не создаётся рядом с первым.
descriptionstring- Фраза о том, для чего нужна роль: с обрезкой пробелов и не длиннее 240 символов. Строка, которая после обрезки оказывается пустой, сохраняется как null, поэтому описание из одних пробелов возвращается как null, а не так, как вы его отправили.
permissionsPermission[]обязательно- Что даёт роль — из словаря, который отдаёт `listPermissions()`; строка не из него даёт 422 по `permissions`, а не отбрасывается молча, поэтому опечатка обнаруживается, а не стоит вам половины дня. Список РАСШИРЯЕТСЯ на входе (`templates:write` сохраняет рядом с собой `templates:read`), дедуплицируется и приводится к каноническому порядку, поэтому читайте сохранённый список из ответа, а не считайте, что это тот же, что вы отправили.
Ответ
object'role'- Всегда `role`. Надгробие удаления отвечает тем же значением, идентификатором роли `id`, `deleted: true` и двумя счётчиками переназначения — и ни одним из остальных полей ниже.
idstring- Идентификатор роли. Это то, что называет `roleId` участника, на что указывает потолок API-ключа и что принимает `reassignTo` при удалении этой роли.
namestring- Имя роли в рабочем пространстве: с обрезкой пробелов, уникальное без учёта регистра. Переименовать можно любую роль, кроме роли владельца, включая засеянные (`builtin` говорит, откуда взялась строка, а не как она обязана называться), поэтому не читайте «Admin» как обещание того, чем роль обладает. Имя, на которое уже отзывается другая роль, даёт `role_name_taken` (409, `param: "name"`); переименование владельца — `role_immutable` (409), как и любая другая его правка.
descriptionstring | null- Фраза, описывающая роль, или null, если она не задана. Пустой ввод сохраняется как null и при создании, и при обновлении, поэтому здесь никогда не бывает пустой строки.
permissionsPermission[]- Всё, что даёт роль, уже в развёрнутом виде и в каноническом порядке, а не в том, в каком это кто-то набрал. Этот порядок несёт нагрузку: две роли с одинаковыми правами сравниваются как равные в виде JSON, и именно это позволяет экрану настроек сравнить их и решить, активна ли кнопка сохранения.
builtin'owner' | 'admin' | 'member' | 'viewer' | 'developer' | 'billing' | null- Из какой из шести засеянных ролей произошла эта строка, или null для роли, которую рабочее пространство создало само. Поле фиксирует происхождение, а не статус: засеянную роль переименовывают, перенастраивают по правам и удаляют, как любую другую. Ветвитесь по `editable` и `deletable`, а не по этому полю. Роль, которую кто-то назвал «Admin», не обязана быть засеянной, а засеянная может уже так не называться.
editableboolean- Вычисляется как `builtin !== 'owner'`, поэтому равно false только для роли владельца, и любой PATCH этой роли отклоняется с `role_immutable` (409). Все остальные роли редактируются полностью (имя, описание и права), включая пять, которыми засевается рабочее пространство.
deletableboolean- Вычисляется как `builtin !== 'owner'`: false только для роли владельца, которая возвращает `role_undeletable` (409), и true для всех остальных ролей, включая засеянные. Проверяйте его до того, как показать кнопку, а не после отказа; при этом роли, которой кто-то ещё обладает, нужен и `reassignTo`, иначе удаление даст `role_in_use` (409).
membersnumber- Сколько людей обладают этой ролью — по строкам участников рабочего пространства. Владельца среди них нет: у него нет строки участника и ему нельзя назначить роль, поэтому роль Owner сообщает о нуле обладателей, хотя список участников его показывает.
apiKeysnumber- Сколько действующих API-ключей ограничено этой ролью; отозванные ключи в счёт не входят, хотя при удалении перенаправляется каждая строка ключа, указывающая на роль, включая отозванные. Это вторая группа, которую надо перенести, прежде чем роль можно будет убрать, и та, которую никто не замечает: ключи — это программы, а программа не жалуется.
createdAtstring- Когда была создана строка роли, ISO-8601. Встроенные строки засеваются лениво, при первой же надобности — при чтении списка ролей, создании роли или открытии экрана API-ключей, — а не при создании рабочего пространства, поэтому отметка времени встроенной роли — это момент того первого запроса, а не момент создания рабочего пространства.
updatedAtstring- Когда роль в последний раз менялась, ISO-8601. Любой принятый PATCH её сдвигает, в том числе тот, что устанавливает полю уже имеющееся значение.