четверг, 19 февраля 2015 г.

Как обоблачиться с YandexDisk

Эту статью я написал потому, что сам очень хотел прочитать такую - о сервисах Яндекса, авторизации OAuth, о том, как держать бубен, чтобы коробочный Indy выполнял запросы по HTTPS. Это всё не сложно на самом деле, но прочти я такую статью раньше - глядишь, и если бы не одним седым волосом в моей бороде было бы меньше, то уж наверняка одним бы волосом на моей макушке - больше. 

Часть I. Теория

2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet

YandexDisk (далее - ЯД, в хорошем смысле) может служить не только в качестве личного облачного хранилища, но и выступать как источник данных для вашего приложения.  Если вам нужно просто иметь доступное отовсюду место, куда можно заглянуть на предмет наличия каких-либо новостей, то бесплатное облако Яндекса может вполне удовлетворить ваши потребности.


В качестве примера давайте сделаем по следам Праздника Влюблённых (или в преддверии Международного Женского Дня) приложение для украшения фоток. Возьмём фотографию с камеры или из галереи, а потом налепим на неё каких-нибудь прикольных штучек. Например, пусть это будут сердечки, цветочки, звёздочки, очки, усы, рамки, буквы... Вот, видите - ассортимент может быть самый разнообразный. Не обновлять же всякий раз приложение, когда мы творчески разродившись добавим в нашу коллекцию приколов новую фишку? Значит, надо дать приложению возможность скачивать эти картинки по мере их создания.

Чтобы построить наше  фишкохранилище давайте заведём аккаунт на ЯД(https://disk.yandex.ru).

Думаю, ввести своё имя и телефон вы сможете без подсказок. Что касается предлагаемого Яндексом клиента для Windows, то тут советую подумать - нужно ли вам, чтобы каждый раз, когда вы сделаете копию экрана или воткнёте в телефон шнурок, выскакивало окошко с просьбой забрать у вас свеженький контент. Меня лично такая настойчивая забота несколько отпугивает. А подключить ЯД к проводнику Windows можно и другим, менее "дружественным" способом - через "Сервис->Подключить сетевой диск". Об этом позднее.

Сейчас же подумаем, как нам дать приложению доступ к данным. Разумеется, в Яндексе об этом уже подумали за нас - они предоставили нам программный интерфейс. И даже не один.


Ну, во-первых, давайте не будем рассматривать "внешние ссылки", т.к. они на самом-то деле ведут не к требуемому файлу, а на страницу скачивания, где искать уже ссылку на исходный файл - задача архисложная для слишком больших любителей. Мы пойдём другим путём(© Владимир Ильич Ульянов) - API Диска.

API Диска предлагает Виджеты Диска, REST API Диска, SDK Диска и WebDAV API Диска. Виджеты нам в приложении вряд ли помогут, SDK (основан на WebDAV) для Delphi пока не сделали, а выбор между REST и WebDAV - однозначно в пользу REST, т.к. о REST слышал даже самый ленивый менеджер проектов, а рассказывать на собеседовании, что вы освоили какой-то "Web-Dove" - ну, мало ли уже в компании всяких Web-разработчиков и Web-дизайнеров, так что не будем путать людей.

Естественно, это  всё шутки. А на самом деле я исхожу из того, что при определении разрешённых действий REST-ЯД даёт возможность ограничить доступ к диску рамками папки приложения. Если учесть, что к одному номеру телефона Яндекс разрешает привязывать только 3 аккаунта, то ограничение приложения своей папкой - весьма значительный фактор. Так что, выбор - очевиден.

Ну что ж, решив, в каком направлении будем двигаться, начинаем. Первым пунктом нам предлагают получить для своего приложения OAuth-токен. OAuth (Open Authorization, я так думаю ©Рубен Вартанович Хачикян) - это что такое? Это когда нам надо получить куда-то доступ, а пароль за нас вводит кто-то другой. Это весьма странная штука, найти аналогию которой в реальном мире довольно затруднительно.



Двигаясь в направлении получения токена не будем тратить время на изучение документации (её читают только от безысходности) и сразу идём регистрировать приложение, где заполняем анкету, в которой обозначим желаемые ресурсы. Их тут, оказывается, довольно много - не только ЯД. Так что мы можем пожелать одновременно и что-то ещё - например, Я-Фотки. Кстати, я обнаружил, что у Яндекса всё ещё нет своего приложения для Я-Телепрограмма - надо как-нибудь потом обязательно сделать.


После регистрации у нас появится ещё не токен, а только идентификатор приложения и плюс к нему - пароль. В этот момент могут возникнуть вполне логичные сомнения: а нужен ли ещё какой-то токен, если уже есть и логин(айди) и пароль? Не одно ли это и то же - этот айди и тот токен?

А где же токен? Вернёмся на шаг назад (кнопка "Редактировать") и ещё раз глянем в низ анкеты - там ясно написано, что будет токен, время жизни которого "Не менее, чем 1 год"!


   
Вот, кажется, и пришла пора ознакомиться с документацией, в которой какие-то схемы взаимодействия, УРЛы обратного вызова и много-много букв. Ну-ну-ну, не будем отчаиваться! Давайте просто немного подумаем, прикинем, что должно происходить в процессе, и наверняка разберёмся без лишних страданий.

Итак, как уже было замечено, айди - это не просто номер приложения, а обозначение списка ресурсов. Причём сейчас стало совершенно очевидно, что ресурсы принадлежат не приложению, а пользователю. Т.е. чтобы добраться до ресурса, нужен логин и пароль пользователя. А логин и пароль пользователя - это доступ не к ресурсам из списка, а ко ВСЕМ ресурсам пользователя, это ПОЛНЫЙ доступ ко всем ресурсам, включая их администрирование. Поэтому, если чисто конкретно для своего аккаунта мы можем вписать в своё приложение логин и пароль, то ждать от стороннего пользователя, чьими, скажем, фотками (только фотками и ничем больше!) мы хотим воспользоваться, что он для этого станет вводить нам в диалог свои входные данные - это, мягко скажем, слишком большие ожидания.

И как же тогда быть? Или точнее - как же они это делают?

"Они" вызывают на помощь Web-интерфейс сервера авторизации Яндекса (или того, кого им нужно), отправив ему при вызове в качестве параметра идентификатор приложения. Да, да, вот так в маленьком окошке браузера открывается Web-интерфейс, где Яндекс показывает  повешенные на идентификатор при регистрации приложения хотелки относительно ресурсов пользователя, а пользователь, видя знакомый логотип в окружении привычных красок и текстов, доверчиво вводит свои учётные сведения и, ознакомившись со всем списком желаний приложения, разрешает (или нет) приложению своими ресурсами пользоваться. 

Да вы наверняка сами часто видели такие окошки, когда какая-нибудь "Забавная ферма" или "Джонни-попрыгунчик" просят вас поделиться своими успехами где-нибудь в FaceBook® или в Одноклассниках®. И, если вы заметили, часто даже и пароль вводить не нужно - обычно он уже давно лежит в куках.
{Здесь должна быть картинка из "Забавная ферма" или "Джонни-попрыгунчик" и авторизация кого-нибудь в чём-то из, но я не играю ни в то, ни в другое, и поэтому у меня нет такой картинки.}
У Яндекса это окошко вызывается УРЛом https://oauth.yandex.ru/authorize?response_type=token&client_id=<идентификатор приложения>[&display=popup][&state=<произвольная строка>]. Добавка display=popup - это чтобы без навигации Яндекса и прочего дополнительного контента. Такой минимализм изначально предназначался для небольших экранов, но на самом деле, я считаю, для диалога в приложении по-другому и быть не должно. Параметр state - это придумываемая автором приложения метка, которая вместе с выдаваемым токеном пойдет по тому УРЛу, что мы задали во время регистрации приложения. Для Web-приложений может быть очень удобно указать свою точку входа, общую для нескольких идентификаторов, и по параметру state судить о том, какое приложение сейчас получило токен. 


Поскольку у нас не Web-приложение, а нормальное настоящее, то нам надо показывать форму с компонентом TWebBrowser, у которого при старте следует вызвать Navigate(<тот самый УРЛ>). Когда нажимается "Разрешить" и уходит POST с формы, обратно возвращается 302 - Temporary moved с "новым" Location https://oauth.yandex.ru/verification_code#access_token=<желанный OAuth-токен>&token_type=bearer&expires_in=31536000. Разумеется, браузер тут же попытается открыть эту страничку, где будет - что? - правильно, наш токен, аккуратно написанный на милой страничке  красивым почерком. Конечно, страничку грузить не обязательно, а достаточно повеситься на событие OnBeforeNavigate2 и вырезать параметр access_token=. Всё!

А вы знаете, что форма с браузером уже есть в готовом виде? Она лежит  в  $(BDS)\Source\data\rest в виде двух модулей REST.Authenticator.OAuth.WebForm - .Win и .FMX. Как ею пользоваться - в примерах Object Pascal\Database\RESTDemo\RESTDemos.dpr. В форму добавлена нерусская кнопка Close, событие редиректа почему-то отлавливается с двух событий - Before и After, а сам обработчик поиска токена в УРЛе в комплектацию формы не входит. Так что, если надумаете пользоваться, советую скопировать модуль с формой в папку проекта и подшаманить, заглядывая в RESTDemos.

А как же компоненты с такими интересными названиями - TOAuth1Authenticator и TOAuth2Authenticator? Увы, господа, эти компоненты ничего не делают. Ну, то есть не то, чтобы совсем ничего, но, скажем так, ничего, что приводит к вызову той самой аутентификации в окошке. Это всего лишь пристёжка к TRESTClient, которая добавляет в параметры уже полученный ранее  OAuth-токен. Често говоря, мне кажется, что делать для этого компонент - как из пушки по воробьям, проще самому добавить параметр ручками. Но тут играют роль общеконцептуальные соображения, т.к. аутентификация может быть не только OAuth.

Ну, вот. Откуда токен - мы знаем: из УРЛа с идентификатором приложения. Что такое OAuth-токен - мы тоже уже понимаем: токен - это идентификатор связки прав доступа с ресурсом пользователя. Срок действия - 31536000 секунд - позволяет нам зашить этот токен в приложение, и уже безо всяких особых форм с браузерами просто работать с Яндексом через REST или другой API.

--------------------------------------
Чтобы найти своё приложение я захожу на ЯД, выбираю "Разработчикам", "REST API Диска", "OAuth-токен" и тычу в. Это почему-то кажется мне проще, чем просто набрать oauth.yandex.ru.



2.1 Подключение ЯД к Windows
2.2 Создание папки приложения
2.3 Indy и HTTPS
2.4 Скрипач как прокси
2.5 Протокольные компоненты, часть 1. TidHTTP и JSON
2.5 Протокольные компоненты, часть 2. TREST~ и TDataSet