Механизм, используемый для авторизации пользователь, не разрабатывается в рамках данной работы, так как используется существующая модель, разработанная для авторизации пользователей в мобильном приложении. Так как одной из функциональных задач расширения является возможность добавления слов на изучение в мобильном приложении, важно использование одного и того же механизма аутентификации, чтобы идентификаторы для одних и тех же пользователей совпадали на разных платформах. Таким образом появляется возможность получения по идентификатору пользователя добавленных через расширение слов в приложении.
Далее будет кратко рассмотрен используемый механизм авторизации в целях понимания того, каким образом данный механизм должен быть реализован в браузерном расширении.поддерживает два типа доступа: авторизованный и неавторизованный. [26] В обоих случаях пользователю выдается уникальный идентификатор, называемый Identity ID. При инициализации SDK Cognito каждый пользователь получает неавторизованный доступ к сервисам AWS, получить авторизованный доступ можно, пройдя механизм аутентификации. Именно в ходе процесса аутентификации Identity ID авторизованного пользователя в Cognito получает привязку к данным, с помощью которых пользователь проходит авторизацию (например, аккаунту в социальной сети или связке логин/пароль). Таким образом, в дальнейшем при авторизации в Cognito с помощью одних и тех же данных пользователь получает один и тот же Identity ID, что дает возможность использовать этот идентификатор для хранения пользовательской информации, например, истории добавленных слов.Cognito поддерживает авторизацию с помощью встроенных обработчиков популярных социальных сетей и сервисов авторизации (Google+, Facebook, Amazon, Twitter), так и предоставляет возможность реализации собственного обработчика регистрации и авторизации пользователей (developer authenticated identities), в случае, если разработчик желает реализовать механизм аутентификации на своем сервере, что полезно в случае, если требуется поддержка соцсети, не поддерживаемой Cognito, или авторизация с помощью логина и пароля.
В случае использования встроенных обработчиков,
схема получения авторизованного доступа и IdentityId в Cognito выглядит как на
рис. 17 [27]:
Рис. 17. Схема авторизации с помощью встроенного
провайдера.
Клиент инициирует процедуру авторизации с поддерживаемой соцсетью и получает авторизационный токен, который после этого отдается SDK Cognito, и дальнейшее получение и обновление токена для доступа к сервисам AWS остается вне зоны ответственности разработчика. В данном случае Cognito занимается так же проверкой токена соцсети и отвечает за выдачу одинакового IdentityId одним и тем же пользователям соцсети.
При использовании механизма авторизации, не
поддерживаемого Cognito, а реализованного разработчиком (developer
authenticated identities), схема авторизации указана на рис. 18:
Рис. 18. Схема авторизации с помощью провайдера
разработчика.
В данном случае за авторизацию (проверку данных, введенных (логин и пароль) или переданных (токен соцсети) пользователем и соотношение их с определенным аккаунтом) отвечает сервер разработчика, который, после проверки, так же должен произвести следующий шаг, который в предыдущей схеме осуществлялся автоматически SDK Cognito - получение OpenID токена от Cognito.
Так как в мобильном приложении необходима поддержка одновременной авторизации с использованием нескольких провайдеров, включая те, что не поддерживаются AWS Cognito, и авторизация с помощью логина/пароля, используется механизм авторизации с помощью провайдера разработчика.
Учитывая эти требования, вышеописанная схема усложняется дополнительным шагом - функцией GetDeveloperIdentityToken, которая принимает от пользователя авторизацонные данные, соотносит их по email-адресу с определенным акаунтом на сервере, и возвращает токен, который используется для авторизации с помощью провайдера разработчика - функции GetIdentityInfo по схеме.
Таким образом, финальная используемая схема
аутентификации пользователя для получения авторизованного доступа к сервисам
AWS через AWS Cognito выглядит следующим образом (рис. 19):
Рис. 19. Используемая схема авторизации.
Разработка программного обеспечения
Серверная часть
Серверная программа была разработана на языке Node.JS с использованием сервиса AWS Lambda. В рамках данной работы было разработано 5 “функций”, каждая из которых представляет собой фактически класс с набором методов. Функции никак не связаны и не общаются между собой.
Каждая функция выполняет некоторый набор
действий, общий для каждой из функций (рис. 20). Так как часть действий
выполняется асинхронно, для того, чтобы гарантировать последовательность
выполнения используется библиотека async [39]. Для общения с используемой базой
данных DynamoDB используется модуль aws-sdk [40], предоставляющий SDK для
доступа к сервисам Amazon AWS.
Рис. 20. Диаграмма активностей функции.
Происходит инициализация библиотек, использующихся функцией.
С помощью модуля input-resolver [41]происходит
проверка формата и целостности данных, поступивших на вход функции (рис. 21).
При несоответствии спецификации возвращается ошибка.
Рис. 21. Пример конфигурации модуля input-resolver.
Происходит получение identityId (идентификатора
пользователя, вызвавшего функцию) из параметров, переданных либо на вход
функции, либо автоматически через SDK AWS Lambda (рис. 22).
Рис. 22. Получение идентификатора пользователя.
Выполняются заявленные конкретной функцией задачи
Возвращается результат, либо отправляется ошибка.
Далее будут рассмотрены процессы, выполняемые каждой из функций для достижения поставленных перед ней задач.
Сохранение слов в истории добавленных на
изучение слов происходит функцией AddWord. Единственной задачей функции
является получение слова от клиента и добавление его в таблицу
ExtensionWordsAdded. Для общения с базой данных используется класс docClient
SDK Amazon DynamoDB (рис. 23).
Рис. 23. Создание записи в таблице на примере
добавления слова.
Получение истории добавленных на изучение слов
обеспечивается функцией GetAddedWords. Данная функция по полученному
идентификатору пользователя выбирает все записи из таблицы, хранящей историю
добавленных слов, и возвращает данные клиенту (рис. 24).
Рис. 24. Получение записей из таблицы по
идентификатору пользователя.
За обработку истории просмотренных страниц пользователя и пополнение статистики о встреченных словах отвечает функция TeachTfIdf. Алгоритм функции выглядит следующим образом:
Получение статистики о встреченных словах из таблицы ExtensionWordsOccurences;
Для каждой страницы, поступившей на вход - получение английских слов со страницы, подсчет и обновление статистики;
Запись обновленной статистики в таблицу.
Важные слова для конкретной страницы возвращает функция GetImportantWords, которая действует так:
Получает статистику о встреченных словах из базы данных;
Получает английские слова из переданной страницы;
Подсчитывает значение меры tf-idf для каждого из слов (рис. 25);
Сортирует полученные данные по значению tf-idf
по убыванию и возвращает N наиболее важных слов (по умолчанию N=10, либо
указывается параметром Limit функции).
Рис. 25. Подсчет меры tf-idf для слова.
Получение английских слов со страницы функциями
TeachTfIdf и GetImportantWords происходит набором методов, вынесенных в общую
библиотеку используемых методов lib.js, которые осуществляют получение
html-содержимого страницы с помощью модуля request [42], получение слов cо
страницы с помощью модуля cheerio [43] и их лемматизацию (рис. 26).
Рис. 26. Диаграмма активностей модуля,
получающего слова со страницы.
Для осуществления лемматизации слов была создана
отдельная функция LemmatizeWord(рис. 27), написанная на языке Python и
используящая библиотеку Natural Language Toolkit (NLTK) [47].
Рис. 27. Лемматизация слова.
Перевод и предоставление дополнительной информации о слове производится функцией TranslateWord. Для получения информации о слове в первую очередь происходит обращение ко внутренней базе данных, при отсутствии слова в ней - обращение к API переводчика Google Translate. После получения данных о слове производится фильтрация словарных значений, содержащих нецензурные фразы и слова используя словарь таких выражений.
Клиентская часть
Исходный код браузерного расширения, как было описано выше, делится на три компонента: browser action, content script и background window. Для облегчения организации процесса разработки используются два компонента: gulp [44] как система, позволяющая автоматизировать процессы сборки и тестирования расширения, и bower [45] как модуль, отвечающий за установку и подключение зависимостей (пакетный менеджер).
Интерфейс расширения
За отображение интерфейса, показывающего историю добавленных и просмотренных слов, а так же реализующего механизм авторизации, отвечает компонент browser action, исходный код которого находится в файле popup,js, а шаблон интерфейса - в файле popup.html. Логика работы данного интерфейса реализуется с помощью Angular.JS: происходит создание модуля wordsApp и контроллера PopupController, который реализует следующие методы:- отвечает за авторизацию через Facebook. Данный метод создает новую вкладку, в которой открывается страница подтверждения доступа к приложению и, при необходимости, авторизации.- возвращает статус слова (просмотрен/добавлен) путем проверки наличия слова в localStorage хранилищах добавленных и просмотренных слов. Данный метод используется для отображения или скрытия кнопки “Учить слово” для слов, уже добавленных на изучение.- очищает историю просмотренных слов путем очистки соответствующего localStorage хранилища.- добавляет слово на изучение.
Интерфейс просмотренных слов использует функцию
двухстороннего связывания данных AngularJS. Отображение добавленных и
просмотренных слов происходит с помощью директивы ng-repeat, которая отображает
html элемент, к которому применена эта директива, для каждого элемента
коллекции данных, переданных директиве в качестве параметра (рис. 28).
Рис. 28. Отображение добавленых слов с помощью
ng-repeat.
Доступ к добавленным и просмотренным словам, хранящимся в localStorage браузера, осуществляется с помощью инжекции сервиса $storage, предоставляемого библиотекой ngStorage [35]. Данная библиотека обеспечивает двухстороннее связывание данных так же для хранилища localStorage.
Отображение или скрытие определенных частей
интерфейса согласно какому-либо условию происходит с помощью директивы ng-show,
принимающей в качестве параметра выражение или функцию, возвращающую true для
показа элемента, и false для скрытия (рис. 29).
Рис. 29. Отображение различных данных в
зависимости от статуса авторизации и пример использования директивы ng-click.
Отображение данных поля, находящегося в зоне видимости контроллера, осуществляется в html шаблоне с помощью синтаксиса {{variable}}, где variable - переменная, которую нужно отобразить.
Таким образом в шаблоне popup.html отрисовывается две вкладки, переключающиеся с помощью ng-show по значению переменной showAdded (true - показывается вкладка с добавленными словами, false - c просмотренными). В каждой вкладке для каждого слова, хранящегося в соответствующем хранилище в localStorage отрисовывается шаблон слова с помощью ng-repeat. Вызов методов clearHistory, fbLogin и addWord происходит по клику пользователя на соответствующую ссылку с помощью директивы ng-click, параметром которой является метод контроллера, который необходимо вызвать (рис. 29).
Скрипт содержимого страницы
За работу всплывающего окна, появляющегося по двойному клику по слове на просматриваемой страницей отвечает скрипт contentscript.js. Интерфейс всплывающего окна отображается по шаблону, содержащемуся в файле context.html.
В первую очередь, данным скриптом создается
новый элемент в теле просматриваемой страницей, в которую подгружается шаблон
интерфейса всплывающего окна (рис. 30).
Рис. 30. Создание нового элемента в теле
документа.
Отслеживание двойного клика по слову происходит
с помощью библиотеки jQuery. После получения информации о подобном событии
осуществляется получение выделенного на странице слова, позиционирование
всплывающего окна согласно позиции выделенного слова, передача информации о
слове в контроллер AngularJS, отвечающий за отображение информации в окне, и
собственно отображение окна (смена стиля элемента с display=none на
display=block) (рис. 31).
Рис. 31. Отображение окна по двойному клику.
Логика работы интерфейса всплывающего окна так же реализуется с помощью AngularJS: создается модуль contentPopupApp с контроллером PopupController, реализующим следующие методы:- добавляет слово на изучение- вызывается извне контроллера по двойному клику на слово, получая выделенное на странице слова, далее подгружает информацию на слове, сохраняя ее в поле word, по которому отображает данные шаблон context.html.- заменяет основной перевод слова по клику на одно из словарных значений в всплывающем окне.
Так как скрипт содержимого страниц выполняется
вне контекста браузерного расширения, а в контексте просматриваемой страницы
(иначе у скрипта не было бы доступа к содержимому страницы), прямой связи с
остальными частями приложения у него нет. Для решения этой проблемы
используется API Message Passing [24] браузера Google Chrome, позволяющего
передавать сообщения и получать ответы на них от других частей расширения.
Данный API используют методы контроллера addWord и loadWordInfo (рис. 32), для
реализации функционала которых требуется вызов методов скрипта фоновой
страницы, отвечающего за общение с серверной частью и хранилищем данных.
Рис. 32. Отправка запроса на перевод слова
скрипту фоновой страницы.
Так же API Message Passing используется в
скрипте содержимого страницы для того, чтобы получать сообщение о необходимости
подсветить важные слова на странице от скрипта фоновой страницы. Для подсветки
слов используется плагин jQuery под названием highlight [32] (рис. 33).
Рис. 33. Подсветка слов на странице
Протокол и формат сообщений, которыми обмениваются части расширения с помощью Message Passing API будет описан далее.
Для обеспечения дополнительной безопасности, например, предотвращения XSS атак, AngularJS работает в режиме, называемом Script Contextual Escaping (SCE). [31] Данный режим предполагает, что в некоторых контекстах привязка данных должна сопровождаться явным указанием на то, что используемое значение безопасно в данном контексте. В скриптах содержимого страницы и интерфейса расширения такого указания требует привязка URL аудиофайла к HTML5 элементу, осуществляющему проигрывание аудио.