Роли
`roles.list`, `list_all`, `iterate`, `get`, `create`, `update`, `delete` и `list_permissions`.
Все методы
from openemail import openemail roles = openemail.roles.list()role = openemail.roles.get('role_…') support = openemail.roles.create({ 'name': 'Support', 'description': 'Answers the shared inboxes and nothing else.', 'permissions': ['emails:send', 'threads:write', 'labels:write'],}) print(support['permissions']) openemail.roles.update(support['id'], { 'permissions': [*support['permissions'], 'templates:read'],}) openemail.roles.delete(support['id'], reassign_to='role_…') vocabulary = openemail.roles.list_permissions()support['permissions'] содержит шесть записей, а не три: emails:send тянет за собой emails:read, threads:write тянет за собой threads:read, а labels:write тянет за собой labels:read. Прочитайте список обратно, а не предполагайте его состав.
Роль говорит, что человеку можно ДЕЛАТЬ. С какими АДРЕСАМИ он может это делать, решает вторая ось, и она живёт в openemail.members. Смотрите там grant_address и revoke_address. «Может отправлять почту» и «может отправлять от имени invoices@» являются разными утверждениями, и рабочее пространство, нанявшее второго специалиста поддержки, меняет второе, не трогая первое. Одно разрешение отвечает на оба: роль с addresses:all охватывает все адреса, включая добавленные позже, без выдачи, и добавить его в роль может только человек в приложении.
Ветвитесь по editable и deletable, а не по имени в builtin. Оба равны false только у владельца, чей список прав означает «все права, включая те, что придумают в следующем году», и он вычисляется, а не хранится; все остальные роли отвечают true на оба, включая пять, которыми засевается рабочее пространство. Переименованная кем-то роль по-прежнему отвечает на оба правильно, а её имя уже ни о чём не говорит.
update ЗАМЕНЯЕТ список прав. Вызова для выдачи одного права нет, поэтому прочитайте роль, измените нужную запись и отправьте обратно весь список. Отправка одного права оставит роль ровно с ним и с тем, что из него следует.
delete требует reassign_to, как только роль кем-то занята, и параметр передаётся в строке запроса, потому что тело в DELETE отбрасывают несколько сред выполнения и ряд прокси. Результат сообщает reassigned и keysReassigned по отдельности, чтобы скрипт мог записать в лог то, что он сделал, а не то, о чём просил.
list_permissions() соответствует GET /roles/permissions, фиксированному пути ровно там, где стоял бы идентификатор роли, поэтому get('permissions') попадает на тот же эндпоинт и отвечает списком разрешений, а не ролью. '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() возвращают и то, и другое, с типами.
Параметры
namestrобязательно- Как рабочее пространство называет роль: от 1 до 48 символов, пробелы по краям обрезаются перед сохранением. Имена уникальны в пределах рабочего пространства без учёта регистра, поэтому второй «Support» отклоняется с `role_name_taken` (409), а не создаётся рядом с первым.
descriptionstr- Фраза о том, для чего нужна роль: с обрезкой пробелов и не длиннее 240 символов. Строка, которая после обрезки оказывается пустой, сохраняется как null, поэтому описание из одних пробелов возвращается как null, а не так, как вы его отправили.
permissionslist[Permission]обязательно- Что даёт роль. Значения берутся из словаря, который отдаёт `list_permissions()`; строка не из него даёт 422 по `permissions`, а не отбрасывается молча, поэтому опечатка обнаруживается, а не стоит вам половины дня. Список РАСШИРЯЕТСЯ на входе (`templates:write` сохраняет рядом с собой `templates:read`), дедуплицируется и приводится к каноническому порядку, поэтому читайте сохранённый список из ответа, а не считайте, что это тот же, что вы отправили.
Ответ
objectLiteral['role']- Всегда `role`. Надгробие удаления отвечает тем же значением, идентификатором роли `id`, `'deleted': True` и двумя счётчиками переназначения, но ни одним из остальных полей ниже.
idstr- Идентификатор роли. Это то, что называет `roleId` участника, на что указывает потолок API-ключа и что принимает `reassign_to` при удалении этой роли.
namestr- Имя роли в рабочем пространстве: с обрезкой пробелов, уникальное без учёта регистра. Переименовать можно любую роль, кроме роли владельца, включая засеянные (`builtin` говорит, откуда взялась строка, а не как она обязана называться), поэтому не читайте «Admin» как обещание того, чем роль обладает. Имя, на которое уже отзывается другая роль, даёт `role_name_taken` (409, `param: "name"`); переименование владельца даёт `role_immutable` (409), как и любая другая его правка.
descriptionstr | None- Фраза, описывающая роль, или null, если она не задана. Пустой ввод сохраняется как null и при создании, и при обновлении, поэтому здесь никогда не бывает пустой строки.
permissionslist[Permission]- Всё, что даёт роль, уже в развёрнутом виде и в каноническом порядке, а не в том, в каком это кто-то набрал. Этот порядок несёт нагрузку: две роли с одинаковыми правами сравниваются как равные в виде JSON, и именно это позволяет экрану настроек сравнить их и решить, активна ли кнопка сохранения.
builtinLiteral['owner', 'admin', 'member', 'viewer', 'developer', 'billing'] | None- Из какой из шести засеянных ролей произошла эта строка, или null для роли, которую рабочее пространство создало само. Поле фиксирует происхождение, а не статус: засеянную роль переименовывают, перенастраивают по правам и удаляют, как любую другую. Ветвитесь по `editable` и `deletable`, а не по этому полю. Роль, которую кто-то назвал «Admin», не обязана быть засеянной, а засеянная может уже так не называться.
editablebool- Вычисляется как `builtin != 'owner'`, поэтому равно false только для роли владельца, и любой PATCH этой роли отклоняется с `role_immutable` (409). Все остальные роли редактируются полностью (имя, описание и права), включая пять, которыми засевается рабочее пространство.
deletablebool- Вычисляется как `builtin != 'owner'`: false только для роли владельца, которая возвращает `role_undeletable` (409), и true для всех остальных ролей, включая засеянные. Проверяйте его до того, как показать кнопку, а не после отказа; при этом роли, которой кто-то ещё обладает, нужен и `reassign_to`, иначе удаление даст `role_in_use` (409).
membersint- Сколько людей обладают этой ролью, считая по строкам участников рабочего пространства. Владельца среди них нет: у него нет строки участника и ему нельзя назначить роль, поэтому роль Owner сообщает о нуле обладателей, хотя список участников его показывает.
apiKeysint- Сколько действующих API-ключей ограничено этой ролью; отозванные ключи в счёт не входят, хотя при удалении перенаправляется каждая строка ключа, указывающая на роль, включая отозванные. Это вторая группа, которую надо перенести, прежде чем роль можно будет убрать, и та, которую никто не замечает: ключи являются программами, а программа не жалуется.
createdAtstr- Когда была создана строка роли, ISO-8601. Встроенные строки засеваются лениво, при первой же надобности (при чтении списка ролей, создании роли или открытии экрана API-ключей), а не при создании рабочего пространства, поэтому отметка времени встроенной роли соответствует моменту того первого запроса, а не моменту создания рабочего пространства.
updatedAtstr- Когда роль в последний раз менялась, ISO-8601. Любой принятый PATCH её сдвигает, в том числе тот, что устанавливает полю уже имеющееся значение.
Коды подтверждения
update и delete спрашивают у токена доступа OAuth код подтверждения, прежде чем что-либо менять, а create не спрашивает. Вызов выбрасывает OpenEmailApiError, у которого is_step_up_required равен True: запросите код через security.begin_step_up(), проверьте тот, что даст вам человек, через security.verify_step_up({'code': ...}), затем сделайте вызов снова. Одно подтверждение действует 60 минут, а у API-ключа код никогда не спрашивают.