Postman: Basic авторизация через скрипт

от автора

Первое изображение для статьи =) привлечение аудитории

Первое изображение для статьи =) привлечение аудитории

Всем привет, меня зовут Алексей Нихаенко и я дата инженер. Это мой первый пост на Хабре и я хочу поведать вам свое более близкое знакомство с инструментом 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 можно двумя способами:

  • Через внутренний функционал по авторизации (тогда заголовок он сам подставляет закодированный)

Базовая авторизация через встроенный механизм Postman

Базовая авторизация через встроенный механизм Postman
  • Либо подставляя закодированный в Base64 значение заголовка Authorization

    Base64 авторизационные данные в заголовке

    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}`);

Результат мы можем увидеть на следующем экране

Результат выполнения скрипта по добавлению заголовка Authorization

Результат выполнения скрипта по добавлению заголовка Authorization

Ниже — скрипты по взятию переменных из коллекции и из окружения

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 нет, однако меня в систему пускает. Почему?

Авторизация и не нужна

Сохранились куки, их нужно чистить перед запросом

Тут, наверное, стоит пояснить — а что значит включенные (активные) \ отключенные заголовки? Объясняю — бывает так, что в одном запросе ты накидаешь несколько заголовков с одинаковым ключом, но разным значением, чтоб в какой-то момент подключать нужный. Вот как это выглядит визуально:

Признак активности заголовка в Postman

Признак активности заголовка в Postman

Прежде чем перейти к тому, как ответил я, давайте разбираться со всем в коде:

  • Хм, как получить значение 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)

Шаг 1. Проваливаемся в Cookies

Шаг 1. Проваливаемся в Cookies
Шаг 2. Проваливаемся в Domains Allowlist

Шаг 2. Проваливаемся в Domains Allowlist
Шаг 3. Добавляем сайт в Domains Allowlist

Шаг 3. Добавляем сайт в Domains Allowlist

Отлично, основные моменты кода вспомнили, теперь давайте определяться с последовательностью действий с учетом вопросов \ нюансов.

  • Перед запросом очищаем все куки

  • Берем активные заголовки 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:

Захардкоженный заголовок не изменяется

Захардкоженный заголовок не изменяется
Запрос не отправился, т.к. 2 активных заголовка Authorization

Запрос не отправился, т.к. 2 активных заголовка Authorization
Отправка запроса без Authorization

Отправка запроса без Authorization
Результат работы скрипта. Заголовок нужный найден и заменен

Результат работы скрипта. Заголовок нужный найден и заменен

И, конечно, я добился того, ради чего все это затевалось, а именно я просто сохранил результат отправки запроса без боязни случайно «спалить» свой логин и пароль

Результат порадовал =)

Результат порадовал =)

Прошу вас, критикуйте, предлагайте, очень буду рад слышать мнение всех вас, золотых моих, умных, чтоб стать и самому лучше и сообществу помочь 🙂


ссылка на оригинал статьи https://habr.com/ru/articles/863318/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *