Mail.ru реализация API, корпоративной почтой

Думаю, что стоит начать с того, что на каждом большом предприятии существует несколько различных сервисов, на которых необходимо управлять данными о сотрудниках. Так и у нас существует Active Directory, на которой лежат учётные записи пользователей от компьютеров со своими групповыми политиками, несколько сетевых хранилищ, система Moodle, корпоративная почта, и другие.

Система работает стабильно, система работает нормально, единственное что при трудоустройстве или увольнении сотрудника, необходимо изменять информацию на каждом из этих сервисов, что предоставляет некоторую проблематичность.

В один из прекрасных дней, мы решили объединить все сервисы в один, так чтобы по верх всего этого работала оболочка, в виде ASP.NET Core, написанная на C#.

Первые трудности

С автоматизацией управления пользователей на Active Directory, сетевых хранилищах и системы Moodle не возникло, поскольку везде есть хорошая документация, с которой можно ознакомиться.

Первые трудности возникли с mail.ru. Почитав информацию в интернете, мы пришли к тому, что можно синхронизировать нашу Active Directory, с их сервисами. Ну и не теряя время нами была отправлена заявка.

Запрос на синхронизацию корпоративной почты с доменом.

Техническая поддержка отреагировала молниеносно и через месяц сообщили что для бесплатных пользователей такой поддержки не имеется. Дайте денег, и будет вам счастье.

Ответ на синхронизацию корпоративной почты с доменом.

Ну, мы люди не гордые, можем и поработать ручками, а именно, реализовать свои запросы к их сервисам, используя HttpClient и WebRequest, на языке C#.

Прокладываем дорогу к получению токена и необходимых данных авторизации

Запускаем программу для сниффера трафика, чтобы посмотреть куда необходимо постучаться, чтобы нам дали данные о входе. Можно использовать большой перечень программ для этого, в частности мною, использовался Fiddler.

Немного посмотрев запросы которые были выполнены при прохождении аутентификации, можно обнаружить то что мы ищем. А именно, запрос на auth.mail.ru. Во вкладке "TextView", можно так-же обнаружить что там передаются данные, которые мы ввели в качестве логина и пароля.

Отлавливаем запрос на авторизацию

Отлавливаем запрос на авторизацию

Для того, чтобы удостовериться в информации, переходим в Postman. И создаём запрос по адресу: https://auth.mail.ru/cgi-bin/auth, отправляя наши данные. Метод передачи POST, в качестве данных отправляем Body => from-data.

Эмулируем запрос

Эмулируем запрос авторизации

Далее видим, что нас перебрасывает по разным доменам, и в итоге мы попадаем на страницу e.mail.ru/messages/inbox/?back=1. В верхнем правом углу можно увидеть что появилось наше имя, а это значит что мы авторизованы на сайте.

Если перейти в раздел Cookies, то можно обратить внимания, что их у нас целая куча. Немного забегая вперёд, скажу что нам понадобятся только несколько из них, это: Mpop, ssdc, ssdc_info и sdcs.

Полученные куки.

Полученные куки.

Куки авторизации получены, идём смотреть запрос на добавление пользователя в корпоративной почте.

Запрос на добавление пользователя

Аналогичным образом поступаем с добавлением пользователя. Но перед этим заглянем в документацию от mail.ru.

Как говорит документация, нам необходимо выполнить POST запрос по адрес: /domains/{domain_id}/users. На это и будем ориентироваться. Сниффим трафик, и добавляем пользователя, чтобы просмотреть, какие данные отправляются.

Отлавливаем запрос на добавление пользователя

Отлавливаем запрос на добавление пользователя

Как видно, запрос действительно выполняется, и в качестве используемых печенек отправляются csrftoken, который является статичным, а так-же Mpop и sdcs (которые мы вроде как получили).

Ну и если переключиться на вкладку "TextView", то можно увидеть что данные на добавление пользователя отправляются по средством Json.

Данные для добавления

Данные для добавления

Далее идём в Postman, чтобы эмулировать наш запрос. В качестве ссылки используем: https://biz.mail.ru/api/domains/{
domain_id }/users. Метод передачи POST. Также добавляем следующие заголовки:

  • Content-Type: application/json
  • Accept: application/json, text/javascript, */*; q=0.01
  • X-CSRFToken: берём из Fiddler
  • Origin и Referer: https://biz.mail.ru

В качестве данных переходим в Body и указываем Raw: {"firstname":"Иван","username":"i.ivanov3","lastname":"Иванов3","password":"sNiOAyUy1o2_"}. Выполняем запрос.

Эмулируем запрос добавления пользователя

Эмулируем запрос добавления пользователя

Как видно из ответа, пользователь успешно добавился. Да и если перейти в админ панель, можно обнаружить что он там действительно есть.

Теперь, если мы перейдём в Cookies, то можно увидеть что у нас появилось две одинаковые печеньки с наименованием sdcs, что означает, что где-то внутри переходов сервис самостоятельно подтягивает ещё куки.

Mail.ru реализация API, корпоративной почтой

Необходимые куки для выполнения

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

Реализация добавления пользователя для корпоративной почты biz.mail.ru на с#.

В первую очередь напишем метод, который будет проходить авторизацию пользователя, и получать наши заветные Cookies.

/// <summary>
/// Token, для использования
/// </summary>
static string token = "";
/// <summary>
/// Создаём Cookie Csrftoken
/// </summary>
public static Cookie Csrftoken = new Cookie("csrftoken", token, "/", "biz.mail.ru");
/// <summary>
/// Создаём Cookie Sdcs
/// </summary>
public static Cookie Sdcs;
/// <summary>
/// Создаём Cookie Mpop
/// </summary>
public static Cookie Mpop;
/// <summary>
/// Создаём Cookie Ssdc
/// </summary>
public static Cookie Ssdc;
/// <summary>
/// Создаём Cookie SsdcInfo
/// </summary>
public static Cookie SsdcInfo;
static void Main(string[] args)
{
    // Создаём задачу = авторизоваться
    Task task = LoginUserAsync("login", "password");
    // Ждём ввода символа
    Console.Read();
}
/// <summary>
/// Авторизация пользователя
/// </summary>
/// <param name="Login">Логин пользователя</param>
/// <param name="Password">Пароль пользователь</param>
/// <returns></returns>
public static async Task LoginUserAsync(string Login, string Password)
{
    // Создаём словарь данных, в котором используем логин и пароль пользователя
    var values = new Dictionary<string, string>
      {
          { "Login", Login },
          { "Password", Password }
      };
    // Создаём Body, в который упаковываем данные
    var content = new FormUrlEncodedContent(values);
    // Создаём контейнер, который будет хранить Cookies
    var cookieJar = new CookieContainer();
    // Задаём обработчик сообщений
    var handler = new HttpClientHandler
    {
        // Указываем Cookies
        CookieContainer = cookieJar,
        // Включаем использование 
        UseCookies = true
    };
    // Создаём http client
    HttpClient httpClient = new HttpClient(handler);
    // Выполняем POST запрос по указанному адресу, с передоваеиым конткном
    HttpResponseMessage response = await httpClient.PostAsync("https://auth.mail.ru/cgi-bin/auth", content);
    // Получаем печеньку Mpop
    Mpop = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["Mpop"];
    // Получаем печеньку Ssdc
    Ssdc = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc"];
    // Получаем печеньку SsdcInfo
    SsdcInfo = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc_info"];
    // Получаем печеньку Sdcs
    Sdcs = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["sdcs"];
}

Запускаем, смотрим. Как не странно, но кука Sdsc, нам не пришла, хотя смотря на Fiddler, или Postman, она точна должна быть.

Mail.ru реализация API, корпоративной почтой

Отсутствие куки Sdcs.

Немного поразмыслив, на ум приходит единственный ответ, что путешествия по сайтам, которые мы наблюдали в Postman, кука где-то удаляется, именно поэтому она не приходит в программу. Исправляем код:

/// <summary>
/// Авторизация пользователя
/// </summary>
/// <param name="Login">Логин пользователя</param>
/// <param name="Password">Пароль пользователь</param>
/// <returns></returns>
public static async Task LoginUserAsync(string Login, string Password)
{
    // Создаём словарь данных, в котором используем логин и пароль пользователя
    var values = new Dictionary<string, string>
      {
          { "Login", Login },
          { "Password", Password }
      };
    // Создаём Body, в который упаковываем данные
    var content = new FormUrlEncodedContent(values);
    // Создаём контейнер, который будет хранить Cookies
    var cookieJar = new CookieContainer();
    // Задаём обработчик сообщений
    var handler = new HttpClientHandler
    {
        // Указываем Cookies
        CookieContainer = cookieJar,
        // Включаем использование 
        UseCookies = true,
        // Отключаем перенаправления
        AllowAutoRedirect = false,
        // Для убедительности, кол-во переходов 1
        MaxAutomaticRedirections = 1
    };
    // Создаём http client
    HttpClient httpClient = new HttpClient(handler);
    // Выполняем POST запрос по указанному адресу, с передоваеиым конткном
    HttpResponseMessage response = await httpClient.PostAsync("https://auth.mail.ru/cgi-bin/auth", content);
    // Получаем печеньку Mpop
    Mpop = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["Mpop"];
    // Получаем печеньку Ssdc
    Ssdc = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc"];
    // Получаем печеньку SsdcInfo
    SsdcInfo = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc_info"];
    // Делаем переход на следующую страницу
    response = await httpClient.GetAsync(response.Headers.Location);
    // Делаем переход на следующую страницу
    response = await httpClient.GetAsync(response.Headers.Location);
    // Делаем переход на следующую страницу
    response = await httpClient.GetAsync(response.Headers.Location);
    // Получаем печеньку Sdcs
    Sdcs = cookieJar.GetCookies(response.Headers.Location)["sdcs"];
}

Проверяем. Кука пришла. Странно, работает, но так точно не должно быть. Дальше пишем ещё один метод, который отправляет данные пользователя на добавление.

Скажу сразу, что тут тоже не всё так просто. Помните что у нас было две куки Sdcs. Так вот чтобы получить ещё одну куку Sdcs, нам необходимо выполнить запрос на добавление. Он вернёт нам ошибку, и куку Sdcs (ту, которая нам нужна). И далее нам нужно отправить ещё один запрос, со всеми собранными куками.

internal class Program
{
    public class User
    {
        /// <summary>
        /// Логин пользователя
        /// </summary>
        public string username = "";
        /// <summary>
        /// Пароль пользователя
        /// </summary>
        public string password = "";
        /// <summary>
        /// Фамилия пользователя
        /// </summary>
        public string firstname = "";
        /// <summary>
        /// Имя пользователя
        /// </summary>
        public string lastname = "";
        public User(string username, string password, string firstname, string lastname)
        {
            this.username = username;
            this.password = password;
            this.firstname = firstname;
            this.lastname = lastname;
        }
    }
    /// <summary>
    /// Token, для использования
    /// </summary>
    static string token = "";
    /// <summary>
    /// Создаём Cookie Csrftoken
    /// </summary>
    public static Cookie Csrftoken = new Cookie("csrftoken", token, "/", "biz.mail.ru");
    /// <summary>
    /// Создаём Cookie Sdcs
    /// </summary>
    public static Cookie Sdcs;
    /// <summary>
    /// Создаём Cookie Mpop
    /// </summary>
    public static Cookie Mpop;
    /// <summary>
    /// Создаём Cookie Ssdc
    /// </summary>
    public static Cookie Ssdc;
    /// <summary>
    /// Создаём Cookie SsdcInfo
    /// </summary>
    public static Cookie SsdcInfo;
    static void Main(string[] args)
    {
        // Создаём задачу = авторизоваться
        Task task = LoginUserAsync("", "");
        // Ждём ввода символа
        Console.Read();
    }
    /// <summary>
    /// Добавление пользователей
    /// </summary>
    /// <param name="user">Пользователь, которого необходимо добавить</param>
    /// <param name="cookieContainer">Куки</param>
    /// <returns></returns>
    static async Task AddUserPostAsync(User user)
    {
        // Собираем контейнер кук
        CookieContainer cookieContainer = new CookieContainer();
        cookieContainer.Add(Mpop);
        cookieContainer.Add(Sdcs);
        cookieContainer.Add(Ssdc);
        cookieContainer.Add(SsdcInfo);
        cookieContainer.Add(Csrftoken);
        // Адрес добавления
        var UrlAddUser = "https://biz.mail.ru/api/domains/{id_domen}/users";
        // Создаём один запрос
        var httpWebRequest = (HttpWebRequest)WebRequest.Create(UrlAddUser);
        // Создаём второй запрос
        var httpWebRequest2 = (HttpWebRequest)WebRequest.Create(UrlAddUser);
        // Задаём заголовки
        httpWebRequest.ContentType = httpWebRequest2.ContentType = "application/json";
        httpWebRequest.Method = httpWebRequest2.Method = "POST";
        httpWebRequest.Accept = httpWebRequest2.Accept = "application/json, text/javascript, */*; q=0.01";
        httpWebRequest.KeepAlive = httpWebRequest2.KeepAlive = true;
        httpWebRequest.Headers.Add("Origin", "https://biz.mail.ru");
        httpWebRequest2.Headers.Add("Origin", "https://biz.mail.ru");
        httpWebRequest.Referer = httpWebRequest2.Referer = "https://biz.mail.ru";
        // Устанавливаем заголовок X-CSRFToken
        httpWebRequest.Headers.Add("X-CSRFToken", token);
        httpWebRequest2.Headers.Add("X-CSRFToken", token);
        // Задаём куки
        httpWebRequest.CookieContainer = httpWebRequest2.CookieContainer = cookieContainer;
        // Добавляем в Body наши данные пользователя
        using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            streamWriter.Write(JsonConvert.SerializeObject(user));
        // Выполняем запрос
        var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
        // Получаем новую сгенерированую куку sdcs
        // У нас уже есть одна, но нам нужна вторая от домена biz.mail.ru
        foreach (Cookie cookie in httpResponse.Cookies)
            if (cookie.Name == "sdcs" && cookie.Domain == ".biz.mail.ru")
                // Добавляем куку в контейнер
                httpWebRequest2.CookieContainer.Add(cookie);
        // Добавляем в Body наши данные пользователя
        using (var streamWriter = new StreamWriter(httpWebRequest2.GetRequestStream()))
            streamWriter.Write(JsonConvert.SerializeObject(user));
        // Выполняем запрос
        httpResponse = (HttpWebResponse)httpWebRequest2.GetResponse();
        var encoding = ASCIIEncoding.ASCII;
        using (var reader = new System.IO.StreamReader(httpResponse.GetResponseStream(), encoding))
        {
            string responseText = reader.ReadToEnd();
        }
    }
    /// <summary>
    /// Авторизация пользователя
    /// </summary>
    /// <param name="Login">Логин пользователя</param>
    /// <param name="Password">Пароль пользователь</param>
    /// <returns></returns>
    public static async Task LoginUserAsync(string Login, string Password)
    {
        // Создаём словарь данных, в котором используем логин и пароль пользователя
        var values = new Dictionary<string, string>
          {
              { "Login", Login },
              { "Password", Password }
          };
        // Создаём Body, в который упаковываем данные
        var content = new FormUrlEncodedContent(values);
        // Создаём контейнер, который будет хранить Cookies
        var cookieJar = new CookieContainer();
        // Задаём обработчик сообщений
        var handler = new HttpClientHandler
        {
            // Указываем Cookies
            CookieContainer = cookieJar,
            // Включаем использование 
            UseCookies = true,
            // Отключаем перенаправления
            AllowAutoRedirect = false,
            // Для убедительности, кол-во переходов 1
            MaxAutomaticRedirections = 1
        };
        // Создаём http client
        HttpClient httpClient = new HttpClient(handler);
        // Выполняем POST запрос по указанному адресу, с передоваеиым конткном
        HttpResponseMessage response = await httpClient.PostAsync("https://auth.mail.ru/cgi-bin/auth", content);
        // Получаем печеньку Mpop
        Mpop = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["Mpop"];
        // Получаем печеньку Ssdc
        Ssdc = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc"];
        // Получаем печеньку SsdcInfo
        SsdcInfo = cookieJar.GetCookies(new Uri("https://auth.mail.ru/cgi-bin/auth"))["ssdc_info"];
        // Делаем переход на следующую страницу
        response = await httpClient.GetAsync(response.Headers.Location);
        // Делаем переход на следующую страницу
        response = await httpClient.GetAsync(response.Headers.Location);
        // Делаем переход на следующую страницу
        response = await httpClient.GetAsync(response.Headers.Location);
        // Получаем печеньку Sdcs
        Sdcs = cookieJar.GetCookies(response.Headers.Location)["sdcs"];
        // Создаём задачу = добавить пользвователя
        Task task1 = AddUserPostAsync(new User("test", "sNiOAyUy1o2_", "test", "test"));
    }
}

Запускаем, проверяем. И как результат, видим успешно созданного пользователя:

Mail.ru реализация API, корпоративной почтой

Созданный пользователь в корпоративной почте

Аналогичным способом мы можем изменять, удалять и получать всю информацию о пользователях домена. Своё готовое бесплатное API для biz.mail.ru готово.

5/5 (2)

Оцените