Авторизация действий #
Приложению необходимо выполнять авторизацию действий пользователя. В рамках учебных приложений реализуем одну из самых простых схем, когда каждому пользователю сопоставляется роль. Конкретной роли предоставляются возможности по выполнению конкретных действий внутри приложения.
Формирование списка действий #
Внутри приложения необходимо определить список действий, для которых необходимо предоставить авторизацию. Причины для авторизации могут быть различными.
- Для выполнения действия необходимо знать человека, который его инициировал. Например при создании объекта необходимо сохранить авторство.
- Выполнение действия должно быть доступно только выделенной группе людей, например владельцам портала.
В рамках приложения по управлению треугольниками можно выделить следующие действия:
- Просмотр списка треугольников.
- Просмотр информации о конкретном треугольнике.
- Добавление нового треугольника в систему.
- Просмотр списка пользователей.
- Добавление нового пользователя.
В приложении можно выделить следующие 3 роли:
- Неавторизованный пользователь, гость.
- Авторизованный пользователь.
- Администратор системы.
Распределим возможности по ролям:
Гость | Авторизованный пользователь | Администратор | |
---|---|---|---|
Просмотр списка треугольников | Да | Да | Да |
Просмотр информации о конкретном треугольнике | Да | Да | Да |
Добавление нового треугольника в систему | Нет | Да | Да |
Просмотр списка пользователей | Нет | Нет | Да |
Добавление нового пользователя | Нет | Нет | Да |
Из таблицы следует, что проверка авторизации на просмотр информации о треугольнике не требуется. И в приложении нет необходимости добавлять дополнительную обработку данных действий.
Описание ролей в приложении #
Можно воспользоваться двумя подходами:
- Описать права роли в перечислении внутри приложения.
- Описать права роли в базе данных.
Вне зависимости от выбранного подхода к реализации описания ролей, в списке пользователей необходимо будет добавить идентификатор роли. Таким образом удобнее будет поместить описание роли внутрь базы данных и работать с ней как с любой другой таблицей в приложении.
Вне зависимости от выбранного пути для каждого пользователя в системе можно будет предоставить объект-роль. У данного объекта следует определить логические поля, отвечающие на вопрос: доступно или нет указанное действие для текущего пользователя.
С точки зрения приложения для работы с треугольниками права можно описать следующим классом данных:
data class Permissions {
val canAddTriangle: Boolean = false,
val canListUsers: Boolean = false,
val canAddUser: Boolean = false,
}
Значения по умолчанию подходят для роли гостя.
Связывание роли и пользователя #
Веб-приложение при каждом запросе к нему должно выполнять процедуру аутентификации пользователя. Обычно она выполняется в фильтрах и использует сессионные токены, расположенные в куках. На основании этой информации следует проводить вычислять роль пользователя. Удобное местоположение - фильтр, следующий за фильтром аутентификации.
Результат вычисления списка разрешений, объект класса Permissions
, следует расположить внутри контекста запроса.
Таким образом для реализации фильтра, вычисляющего разрешения для конкретного пользователя, необходимо передать.
- Линзу для получения объекта, описывающего аутентифицированного пользователя.
- Операцию из базы данных для получения объекта, описывающего права (роль) текущего пользователя.
- Линзу для записи информации о разрешениях пользователя.
Рекомендуется сделать так, чтобы операция по формированию объекта с разрешениями, всегда возвращала корректный объект, который можно записать в контекст запроса.
Уровень реализации авторизации #
Авторизация в приложении может быть реализована на различных уровнях:
- Авторизация на уровне маршрутизатора.
- Авторизация на уровне обработчика запроса.
- Авторизация внутри конкретной операции.
Также необходимо помнить, что внутри пользовательского интерфейса не надо предоставлять возможности по выполнению действий, которые не доступны текущей роли. Это является необходимым уровнем защиты, но не является достаточным. Проверку на стороне обработки HTTP-запросов всё-равно необходимо выполнять.
Таким образом информация из контекста запроса должна учитываться на уровне маршрутизатора и шаблонизатора.
Проверка авторизации на уровне маршрутизатора #
Для реализации проверок на уровне маршрутизатора можно использовать специальный фильтр, который следует вызывать перед конкретным обработчиком запроса. Для работы фильтра ему необходимо передать:
- Линзу для извлечения информации о правах текущего пользователя.
- Функцию, которая будет отвечать на вопрос: разрешено ли выполнить нужную нам операцию.
Внутри фильтра необходимо получить ссылку на объект, описывающий разрешения пользователя, применить к нему функцию по проверке прав. Если функция вернула ложь, то необходимо вернуть ответ с кодом 403, доступ запрещён. Если функция вернула правду, то необходимо передать задачу по обработке запроса следующему обработчику.
Таким образом интерфейс функции по созданию фильтра будет выглядеть следующим образом:
fun permissionFilter(
permissionLens: RequestContextLens<Permissions>,
canUse: (Permissions) -> Boolean,
)
В качестве второго аргумента можно передать любой функциональный тип, который будет выполнять проверку необходимых функций. При использовании классов данных такие функции описать достаточно просто:
routes(
"/user/new" bind Method.GET to permissionFilter(permissionLens, Permissions::canAddUser).then(
addNewUserHandler(...)
),
)
Но если простого чтения свойства из класса недостаточно, то можно описать функцию произвольной сложности.
Учёт разрешений в виде #
Для учёта разрешений на уровне представления необходимо передать линзу для получения объекта со списком разрешений из контекста запроса.
Задача #
Реализуйте систему для проверки прав доступа внутри приложения по управлению треугольниками.
Для этого необходимо:
- Добавить новую таблицу для сохранения разрешений для каждой роли.
- Заполнить базу двумя записями: авторизованный пользователь и администратор.
- Связать существующие учётные записи с одной из данных ролей.
- Добавить новый фильтр для получения списка разрешений для конкретного пользователя.
- Интегрировать данный фильтр в цепочку фильтров, установить его после фильтра аутентификации.
- Модифицировать маршрутизатор приложения таким образом, чтобы соответствующие маршруты были доступны только при наличии соответствующего разрешения.
- Модифицируйте подсистему отображения HTML-документов так, чтобы она не показывала ссылки на маршруты, которые не доступны при текущем уровне доступа.
- Проверьте, что приложение по-разному показывает интерфейс:
- если пользователь не вошёл в систему;
- когда пользователь авторизовался как обычный пользователь;
- когда пользователь авторизовался как администратор.
- Выйдите из системы. Попытайтесь открыть список пользователей. Как повела себя система?
- Доработайте процедуру добавления новых пользователей, чтобы администратор мог указать роль нового пользователя.