Всем привет, меня зовут Алексей Нихаенко и я дата инженер. Это мой первый пост на Хабре и я хочу поведать вам свое более близкое знакомство с инструментом Postman.
О чем пойдет речь?
-
Что такое базовая авторизация и способы использования внутри Postman
-
Задача, которую я преследовал и зачем понадобилась автоматизация (Pre-Request Script)
-
Простые примеры скрипта с Basic Authorization в Pre-Request Script
-
Строим дерево вариантов поведения скрипта
-
Итоговый скрипт, заключение
1. Что такое базовая авторизация и способы использования внутри Postman
Базовая авторизация (Basic Auth) — авторизация, использованная с помощью имени пользователя (username ) и пароля (password ). Отправляется на сервер заголовком (header ) шаблонно вот так:
'Authorization': f'Basic {base64(username:password)}'
Где:
-
Authorization — наименование заголовка (ключ)
-
f»Basic {base64(username:password)}» — закодированное в base64 пара имя_пользователя:пароль
Например, если имя пользователя у нас UserNameRandom а пароль AnyPassword, то вот так по итогу будет выглядеть заголовок:
'Authorization': 'Basic VXNlck5hbWVSYW5kb206QW55UGFzc3dvcmQ='
Закодировать (Encode) в Base64 можете сами онлайн ССЫЛКА
Декодировать (Decode) из Base64 в строку можете сами онлайн ССЫЛКА
Авторизоваться базово внутри Postman можно двумя способами:
-
Через внутренний функционал по авторизации (тогда заголовок он сам подставляет закодированный)
-
Либо подставляя закодированный в Base64 значение заголовка Authorization
Base64 авторизационные данные в заголовке
Задача, зачем нужна автоматизация
Есть Jira. Некоторые запросы должны получать задачи (GET-запросы), некоторые — переводить статус задачи в другой статус (в моем случае из «Новый» в «Отменен»). Я со своей автоматизацией хотел покрыть несколько задач:
-
Хочу попробовать отправить запрос с правильными логинами-паролями
-
Отправить запрос с неправильными логином-паролем
-
Отправить запрос без заголовка Authorization
-
Максимально защитить логины-пароли, чтобы, делясь коллекцией, я «не палил» их
-
Сохранить результаты запросов в Example максимально секурно (см. пункт выше) и чтобы было понятно — использовался ли заголовок авторизации и какой
В связи с вышеизложенным мне хотелось как можно меньше ручных манипуляций. В голову пришла фича Postman, с помощью которой можно до отправки запроса устанавливать заголовки (и вообще много чего еще) в автоматическом режиме с помощью скриптинга на JavaScript — Pre-Request Script (простите за тавтологию, постараюсь больше не использовать)
Простые примеры скрипта с Basic Authorization
Прежде чем перейти к скриптингу, нужно подумать — а где мы будем хранить логины-пароли? На ум приходят три вещи:
-
Переменные внутри коллекции (не безопасно)
-
Переменные внутри скрипта (еще хуже, сложно для поддержки еще к тому же)
-
Переменные внутри окружения Postman (то, что нужно)
Переменные внутри окружения безопасны за счет не только того, что они скрыты визуально (если вдруг попадут в скрин), но и при «поделиться коллекцией» вы не передаете Credentials (учетные данные, логин-пароль)
Следующий вопрос — а куда помещаем скрипт? Я отвечу сразу — на уровне коллекции, чтобы при создании новых запросов заголовок автоматически применялся
Еще важный момент — внутрь коллекции я создал переменную base_url со значением «https://jira.com». В запросах могут фигурировать {{base_url}} — это Postman берет переменную из коллекции и применяет в качестве URL запроса
Итак, приступаем к кодингу. Начнем с самого простого, пойдем потом на усложнение.
Представим, что у нас переменные внутри скрипта, никаких заголовков не устанавливалось на уровне запросов, тогда нам нужно добавить заголовок. Обратите, пожалуйста, внимание на различие add и upsert (в будущем, на усложнении понадобится)
// Зашитые внутрь скрипта креденшелы const login = "UserNameRandom"; const password = "AnyPassword"; // Кодирование в base64 const encodedCredentials = Buffer.from(`${login}:${password}`).toString('base64'); // Установка заголовка Authorization // add используется для добавления заголовка // upsert используется для обновления (хотя должен для вставки и обновления) pm.request.headers.add({ key: "Authorization", value: `Basic ${encodedCredentials}` }); // Выводим опционально значение в консоль Postman console.log("Заголовок Authorization добавлен:", `Basic ${encodedCredentials}`);
Результат мы можем увидеть на следующем экране
Ниже — скрипты по взятию переменных из коллекции и из окружения
var login = "UserNameRandom"; var password = "AnyPassword"; console.log(`Зашитые внутри скрипта логин: ${login} и пароль ${password}`); login = pm.collectionVariables.get("login"); password = pm.collectionVariables.get("password"); console.log(`Переменные коллекции, логин: ${login} и пароль ${password}`); login = pm.environment.get("login") password = pm.environment.get("password"); console.log(`Переменные окружения, логин: ${login} и пароль ${password}`);
Строим дерево вариантов поведения скрипта
Теперь, когда какой-никакой, а скрипт есть, нужно его улучшить, учитывая несколько вопросов \ нюансов:
|
Вопрос |
Варианты поведения |
|
Переменные login или password отсутствуют |
Запрос отправляется без изменений в заголовках |
|
|
Прерываем работу и скрипта и запроса |
|
|
|
|
Заголовок Authorization отсутствует |
Добавляем |
|
|
Не добавляем, запрос отправляется |
|
|
Не добавляем, запрос НЕ отправляется |
|
|
|
|
Заголовок Authorization присутствует |
Изменяем его в любом случае |
|
|
Изменяем его по условию |
|
|
Не изменяем никогда, запрос отправляется |
|
|
Не изменяем никогда, запрос НЕ отправляется |
|
|
|
|
Заголовок Authorization присутствует, но он отключен |
Нужно учитывать только включенные |
|
|
|
|
Заголовков Authorization несколько и все они активные |
Выдавать ошибку, что заголовков несколько |
|
|
Брать какой-то 1 заголовок по условию и см. выше — изменять ли его? |
|
Заголовка Authorization нет, однако меня в систему пускает. Почему? |
Авторизация и не нужна |
|
|
Сохранились куки, их нужно чистить перед запросом |
Тут, наверное, стоит пояснить — а что значит включенные (активные) \ отключенные заголовки? Объясняю — бывает так, что в одном запросе ты накидаешь несколько заголовков с одинаковым ключом, но разным значением, чтоб в какой-то момент подключать нужный. Вот как это выглядит визуально:
Прежде чем перейти к тому, как ответил я, давайте разбираться со всем в коде:
-
Хм, как получить значение login-password из переменных окружения?
// Получение переменных из окружения const login = pm.environment.get("login"); const password = pm.environment.get("password");
-
Как определить — есть ли и логин и пароль? А как в скрипте написать сообщение об ошибке не прерывая работу? А прерывая?
if (!login || !password) { // Определяем переменную для вывода в консоль var reason_error = "Не найдены переменные login или password" // Выводим лог в консоль. Не прерывает работу ни скрипта, ни запроса console.error(reason_error); // Прерываем работу, выбрасывая ошибку throw new Error(reason_error) }
-
Хм, а как посмотреть отключенные заголовки Authorization?
// Наименование (ключ) заголовка var header_name = "Authorization" // Смотрим на отключенные заголовки header_name const disabledHeaders = pm.request.headers.filter(header => header.disabled && header.key.toLowerCase() === header_name.toLowerCase()); // Считаем количество отключённых заголовков header_name const disabledCount = disabledHeaders.length; if (disabledCount >= 1) { console.log(`Количество отключенных заголовков ${header_name} = ${disabledCount}`) } else { console.log("Нет отключенных заголовков") }
-
Хм, а как посмотреть включенные заголовки Authorization? Да то же самое, только вместо header.disabled пишем !header.disabled
-
А как посмотреть вообще все заголовки?
const authHeader = pm.request.headers.get("Authorization");
-
А как чистить куки? А вот это интересный вопрос, потому что:
-
Код выглядит просто:
// Очищаем cookies перед выполнением всех шагов const var_cookies = pm.cookies.jar(); var_cookies.clear(pm.request.url, function (error) { // error - <Error> }); -
Но перед этим нужно добавить сайт в Domains Allowlist (без http/https)
-
Отлично, основные моменты кода вспомнили, теперь давайте определяться с последовательностью действий с учетом вопросов \ нюансов.
-
Перед запросом очищаем все куки
-
Берем активные заголовки Authorization без учета регистра
-
Если их больше 1 — выдаем ошибку, прерываем работу скрипта и запроса
-
Если их 0 — значит так и задумано пользователем, просто отправляем запрос дальше
-
Если их 1 — обновляю только в случае, если в значении встречаются <*calc*>, где * — любой символ и не чувствителен к регистру
-
<Calculated By Script> — заменится значение, т.к. встречаются все символы
-
<CALC> — тоже заменится. Напомню, что ищем не чувствительное к регистру
-
<Any> — Не заменится
-
Symbol<calc> — не заменится, т.к. символ «<» не идет первым
-
<calc>symbol — не заменится, т.к. символ «>» не идет последним
-
-
Идем получать переменные окружения. Если чего-то нет — ошибка, прерываем все.
Таким образом мы:
-
прерываем возможность несколько активных Authorization засунуть в запрос
-
даем возможность без Authorization отправить запрос
-
даем возможность со своим Authorization отправить запрос (он же бывает не только Basic, правильно?)
-
когда нам действительно нужны логин и пароль — проверяем их наличие и выдаем ошибку, если чего-то нет
Итоговый скрипт, заключение
Что-то мне кажется, что за меня все скажет итоговый скрипт, который я использую
// ==================================================== // ==================================================== // ТУТ НИЧЕГО НЕ ТРОГАТЬ!!! // ==================================================== // ==================================================== // Функция для установки заголовка Authorization function setAuthorizationHeader() { // Получение переменных из коллекции const login = pm.environment.get("login"); const password = pm.environment.get("password"); if (!login || !password) { var reason_error = "Не найдены переменные login или password" // Логируем с прерыванием throw new Error(reason_error) // Логируем с продолжением работы скрипта // console.error(reason_error); return; } // Кодирование в base64 (используем Buffer для совместимости с Node.js) const encodedCredentials = Buffer.from(`${login}:${password}`).toString('base64'); // Установка заголовка Authorization pm.request.headers.upsert({ key: "Authorization", value: `Basic ${encodedCredentials}` }); // console.log("Заголовок Authorization добавлен:", `Basic ${encodedCredentials}`); } // Очищаем cookies перед выполнением всех шагов const var_cookies = pm.cookies.jar(); var_cookies.clear(pm.request.url, function (error) { // error - <Error> }); // Наименование (ключ) заголовка var header_name = "Authorization" // Фильтрация отключенных заголовков header_name const disabledHeaders = pm.request.headers.filter(header => header.disabled && header.key.toLowerCase() === header_name.toLowerCase()); // Считаем количество отключенных header_name const disabledCount = disabledHeaders.length; // Фильтрация включенных заголовков header_name const enabledHeaders = pm.request.headers.filter(header => !header.disabled && header.key.toLowerCase() === header_name.toLowerCase()); // Подсчёт количества включенных header_name const enabledCount = enabledHeaders.length; console.log("Количество отключённых заголовков Authorization:", disabledCount); console.log(`Количество включенных заголовков Authorization: ${enabledCount}`); // ==================================================== // ==================================================== // РЕДАКТИРОВАТЬ ТОЛЬКО БЛОК НИЖЕ // ==================================================== // ==================================================== // Если количество отключенных заголовком Authorization НЕ РАВНО включенным //if (disabledCount !== enabledCount) { if (enabledCount > 1) { throw new Error(`Заголовков ${header_name} больше 1. Заголовок должен быть таким 1 в запросе`) } else if (enabledCount === 0) { console.log("Запрос будет отправлен без заголовка Authorization") } else { // Создаем регулярное выражение для поиска "calc" без учёта регистра const regex = /<.*calc.*>/i; // Проверяем, есть ли вхождение в переменной первого элемента (и единственного) enabledHeaders if (regex.test(enabledHeaders[0].value)) { // console.log('Вхождение "<*calc*>" найдено в значении заголовка.'); console.log(`Заголовок ${header_name} будет изменен согласно скрипту`); // Установка заголовка setAuthorizationHeader(); } else { console.log("Активный заголовок Authorization:",enabledHeaders[0].value); } }
И вот несколько скриншотов работы Postman:
И, конечно, я добился того, ради чего все это затевалось, а именно я просто сохранил результат отправки запроса без боязни случайно «спалить» свой логин и пароль
Прошу вас, критикуйте, предлагайте, очень буду рад слышать мнение всех вас, золотых моих, умных, чтоб стать и самому лучше и сообществу помочь 🙂
ссылка на оригинал статьи https://habr.com/ru/articles/863318/
Добавить комментарий