Аутентификация и Авторизация¶
Авторизация/аутентификация производится с помощью HTTP заголовков
Authorization
или Ticket
. Для разных типов клиентов используется
разная схема аутентификации и авторизации. В случае каких-либо проблем с
аутентификацией, возарвщается код ответа 401
, а при проблем с
авторизацией возвращается ответ с кодом 403
.
Использование заголовков¶
Внутренние клиенты (TVM)¶
Внутренними клиентами считаются сервисы Яндекса, для которых не нужно получать OAuth токены для манипуляции данными пользователя. Примеры таких клиентов:
Yamb
Диск
Почта
Портал директории
…
Внутреним клиентам рекомендуется “ходить” в API Директории с TVM тикетами.
Для авторизации по тикету необходимо сопроводить запрос HTTP заголовком
X-Ya-Service-Ticket
следующего вида:
X-Ya-Service-Ticket: {ticket}
После того, как вы завели своё приложение на https://tvm.yandex-team.ru,
надо создать в очереди DIR тикет, с указанием
client_id
вашего TVM приложения, названием сервиса и коротким названием
сервиса, состоящим из латинских букв, цифр и знака -
.
TVM Тикет может содержать uid пользователя. Если он не содержит uid, а ручка
которую вы вызываете, требует наличия пользователя, то uid надо передать
в заголовке X-UID
.
Warning
Если пользователь использует мультиавторизацию, и вы меняете его куки на TVM тикет, то Директория получит из него больше одного уида. В этой ситуации, Директория будет считать запрос неаутентифицированным и вернёт 401.
Чтобы этого не произошло, лучше всегда явно указывать UID текущего пользователя
через заголовок X-UID
.
В любом случае, если ручке нужно работать от имени пользователя, то следует
дополнительно прокинуть его реальный IP в заголовке X-User-IP
:
X-UID: 123
X-USER-IP: 1.2.3.4
Для некоторых ручек, например тех, которые читают данные про организацию,
пользователь не требуется, и нужно указать лишь ID организации в
заголовке X-Org-ID
:
X-Org-ID: 12345
В случае, когда передан лишь id организации, будут работать только
ручки, отдающие данные про компанию. Выполнять какие-либо действия
необходимо от имени какого-то сотрудника организации, а для этого
следует передавать X-UID
и X-USER-IP
.
В случае, если нужно получить данные от uid-а, который был Админом в ПДД (обычный пользователь яндекса), то нужно передавать и X-UID, и X-ORG-ID, т.к. у админов ПДД было несколько доменов и может быть несколько организаций. Такого пользователя мы называем Внешним Админом организации, логика и связанные таски тут
Внешние клиенты (OAuth)¶
Для аутентификации по OAuth токену необходимо сопроводить запрос HTTP заголовком Authorization
следующего вида:
Authorization: OAuth {token}
Логика под капотом¶
При аутентификации запроса, мы выясняем, от какого Сервиса и Пользователя он пришёл.
Под сервисом тут понимается некий сервис, чей идентификатор (OAuth client_id, TVM client_id или же токен зашитый в конфиг) известен Директории.
При этом могут быть запросы:
Анонимные, для которых неизвестен ни Сервис, ни Пользователь.
Такие, где Сервис известен, а Пользователь нет.
Такие, где известен и Сервис и Пользователь.
Такие, где известен Пользователь, но не сервис.
Последний случай может быть, когда некто завёл OAuth приложение, которое не добавлено в Директорию как сервис, и выписал для него OAuth токен от имени Пользователя.
Аутентификация¶
В результате аутентификации, мы ассоциируем с текущим запросом атрибуты
service
, user
, org_id
и scopes
, первые два могут быть
словарями с информацией, или же принимать значение None
, а атрибут
scopes
– set из строк со скоупами, привязанными к сервису.
Атрибут org_id
заполняется в том случае, если передан
заголовок X-Org-ID
.
Сервис известен, а Пользователь нет¶
Такая ситуация получается, в нескольких случаях:
Сервис пришёл с выданным ему когда-то токеном, который захардкожен в конфиге.
Сервис пришёл с OAuth токеном, и Директория нашла его
client_id
в своей базе.Сервис пришёл с TVM токеном, и Директория нашла его
tvm_client_id
в своей базе.
В случае с OAuth и TVM токеном, список скоупов доступных приложению, достаётся сначала из базы Директории, а потом, если это OAuth, то к нему добавляются скоупы отданные Паспортом. Это позволяет нам иметь TVM сервисы с разным набором привилегий, а так же специальные OAuth Сервисы со скоупами, которые не доступны людям снаружи Яндекса.
Анонимные запросы¶
Для анонимных запросов мы просто заполняем service=None
,
user=None
, scopes=set()
.
Авторизация¶
На этом этапе Директория может возвращать как 401
ошибки, так и 403
.
401
возвращается в тех случаях, когда что-то не так с переданными в API данными – например организация или пользователь с указанным id не существуют.403
означает, что данному сервису или пользователю по тем или иным причинам недоступна текущая операция. Например, для создания Команды требуется наличие пользователя, но Сервис не указал от имени кого выполняется запрос. Либо Сервис указал сотрудника, от чьего имени выполняется запрос, но не при этом не имеет полномочий (скоупаwork_on_behalf_of_any_user
).
Сначала Директория вынимает из метабазы информацию про пользователя.
И если был явно указан X-Org-Id
, то проверяет, что пользователь состоит
в этой организации. Если нет, то возвращается 403 ошибка.
Далее, Директория смотрит со сколькими организациями связан пользователь. Если
организация одна, то её id прописывается в org_id
.
Если организаций несколько, то org_id
остаётся None
.
Если org_id
указан, но пользователь не известен, то Директория
проверяет, есть ли такая организация в базе, и если нет, то возвращает
401 ошибку Organization does not exist
.
Таким образом, org_id
либо None, либо соответствует id организации
из базы Директории.
Если org_id
так или иначе стал известен, то Директория выполняет ещё
ряд проверок:
Достаёт организацию из метабазы и узнаёт её шард.
Если организация в процессе миграции (атрибут
ready==False
), то возвращает403 Organization is not ready
.Получает ревизию организации.
Если известен Сервис, то проверяет, что он подключен для данной организации либо имеет скоуп
work_with_any_organization
. Если ни того, ни другого нет, то403 Service is not enabled
.Если известен и Сервис и Пользователь, то проверяет, что сервис имеет скоуп
work_on_behalf_of_any_user
, либо Пользователь - робот, заведённый Директорией специально для этого сервиса. Если нет, то403 Unable to work on behalf of the user
.Дальше проверяется, что если ручка API требует наличия пользователя, то он известен, если нужно знать
org_id
, то он известен, а так же то, что если ручка внутренняя, то сервис должен иметь специальную пометкуis_internal=True
. Если “внешний” сервис пытается получить доступ к внутренней ручке, ему возвращается404 Not found
.
Права на выполнение отдельных ручек, проверяются специальными декораторами и внутри самих ручек. Далее описаны общие правила того, как это происходит.
Соответствуют ли скоупы?¶
Обычно, скоупы на ручки у нас двух типов, к примеру users_read
,
users_write
. Скоупы заканчивающиеся на _read
, говорят, что можно
ходить с запросами в читающие ручки, относящиеся к данной категории, а
скоупы заканчивающиеся на _write
говорят о том, что можно ходить и в
читающие и в пишущие ручки. Поэтому все читающие ручки про пользователей
помечены, что они требуют либо users_read
, либо users_write
, а
пишущие помечены только скоупом users_write
.
Получается, что у запроса должен быть хотя бы один из скоупов, нужен
ручке. К примеру, если вызывается ручка GET /users/
, то у запроса
должен быть либо users_read
, либо users_write
, но могут быть и
оба, хотя в случае, когда есть _write
, _read
скоуп иметь не
обязательно.
Итак, на первом этапе, проверяются скоупы. Если пересечение скоупов требуемых ручкой и скоупов которые есть у запроса пустое, то отвечаем 403.
Имеет ли пользователь право на выполнение действия?¶
Если ручка вызывается от имени пользователя, то дополнительно проверяются его permissions, которые вычисляются исходя из следующих правил:
внешним администраторам позволено всё;
администраторам организации позволено всё кроме отката организации обратно в ПДД;
команды создавать может любой сотрудник;
редактировать команду может только тот сотрудник, который её создал;
сотрудник, редактируя данные о себе, может менять только контакты, день рождения и аватарку;
сотрудник с логином yandex-yamb-support-user лишён всех прав и редактировать он тоже ничего не может.
Для yandex-team организации, синхронизирующейся со стаффом, отключена возможность редактирования отделов и сотрудников. Даже для админов.
Дальше, у каждой ручки перечислен список прав, которые требуются, чтобы действие было произведено. Могут требоваться либо все эти права, либо хотя бы одно.
В сложных случаях, как например редактирование пользователя, когда
только внутри метода можно проверить, какие именно поля редактируются,
проверка прав осуществляется не в декораторе а в самой view. Скажем,
ручка PATCH /users/
внутри смотрит, изменяется ли поле birthday
и если да, то проверяет, есть ли у пользователя право edit_birthday
.