smsGate-1.0.0. Авторизация через sms



На самом деле компонент предназначен не только для авторизации через sms, а в принципе для отправки sms. Но в первую очередь он нужен был именно для настройки двухфакторной авторизации. И вот как раз в плане вклинивания в процесс авторизации я узнал много нового (хотя и так знал не мало). Был немного расстроен…

MODX расстроил меня неидеальностью своего процессора авторизации. Вот смотрите, вот здесь вызывается событие, на которое логично навесить плагин, на уровне которого можно было бы реализовывать свою логику.
$canLogin = $this->fireOnAuthenticationEvent();
Но проверка пароля идет только на следующей строчке.
$preventLogin = $this->checkPassword($canLogin);

То есть в момент срабатывания плагина мы еще не знаем верный пароль или нет. В итоге приходится в плагине выполнять дополнительную проверку пароля.
Но это не главная проблема. Главная проблема в том, что MODX не проверяет успех у вас в плагине или нет, а точнее он просто возвращает false в случае если не успех.

При этом, если внимательно изучить логику, видно, что в случае возникновения ошибок в плагине, пользователь вопреки всякой логике будет на самом деле авторизован, а не послан, так как пароль просто не будет проверяться. Вот куски кода:
public function checkPassword($rt) {
        /* check if plugin authenticated the user */
        if (!$rt || (is_array($rt) && !in_array(true, $rt))) {
            /* check user password - local authentication */
            if (!$this->user->passwordMatches($this->givenPassword)) {
                $this->failedLogin();
                return $this->modx->lexicon('login_username_password_incorrect');
            }
        }
        return false;
    }
$canLogin = $this->fireOnAuthenticationEvent();
        $preventLogin = $this->checkPassword($canLogin);
        if (!empty($preventLogin)) {
            return $this->failure($preventLogin);
        }
Как видите, только в случае успеха выполнения плагина, проверяется пароль. А если нет, то возвращает false. А false !== !empty(false).

Можете сами проверить. Создайте у себя плагин на событие OnWebAuthentication и пропишите в нем $modx->event->_output = 'false';
А в консоли выполните такой код:
$modx->switchContext('web');
 
$response = $modx->runProcessor('security/login', array(  
    "username"  => "test",
    "password"  => "asdasdasddqwdqwdq",
));

print_r($response->getResponse());
Пользователь само собой должен существовать.

Как результат: авторизация. joxi.ru/Y2LjLVESn3pqXr

Надеюсь, кто-нибудь отправит багфикс. Если нет, я позже отправлю.

В общем, вот эти косяки не позволили через плагин реализовать, пришлось расширять процессор и через него работать. Вот код процессора.

Напомню, что компонент Login не использует процессор security/login, там у него все на уровне хуков делается.

Второй неприятный момент был уже с самими sms-шлюзами.

Неприятность 1. Первым делом глаз положил на iqsms.ru/
Вроде и сайт не плохой, и API есть в форматах XML и JSON, да и вообще вроде как все ОК. Сделал я их, SMSки отправляются… Но когда пошел пополнять баланс, а там раз, и минимальный платеж 2000 рублей… Вот куда столько? Я так прикинул, если пара человек в день авторизуется на сайте (шлет штук 5 смс в день), так это на смски вложение на пару лет минимум:) В общем, пошел я искать sms-провайдера, где пополнять можно на меньшую сумму. Нашел letsads.com/ Эти хоть на 5 рублей пополняй. Правда при оплате картой комиссия составила 14%, буду еще смотреть что при оплате яндекс.деньгами будет, там два провайдера платежей у них.

С этим провайдером только маленькая досада, что API в XML-формате, но это мелочи. К слову, я вот как-то писал уже про MODX-овый рест-клиент, вот здесь покажу как приятно через него работать и в таких случаях.

Вот вызов с передачей данных в JSON-формате:
if(!$client = $modx->getService('rest.modRestCurlClient')){
    return "Не был получен CURL-клиент";
}

$params = array(
    "statusQueueName"   => $statusQueueName,
    "statusQueueLimit"   => $limit,
);

$options = array(
    "contentType"   => "json",
);

if($login){
    $options[modRestClient::OPT_USERPWD] = "{$login}:{$password}";
}

if(
    $result = $client->request("{$host}:{$port}", '/statusQueue/', 'POST', $params, $options)
    AND $result = json_decode($result, 1)
){
    return $result;
}
Вот здесь и данные при отправке кодируются в JSON, и авторизация сразу передается.

А чтобы этот же запрос ушел не в JSON, а в XML, надо просто «contentType» => «json» заменить на «contentType» => «xml».

Правда удобно? А полученный XML-ответ легко перевести в массив $array = modRestArrayToXML::toArray($result);

Неприятность 2. Международные и прямые sms-каналы.
Вот это вообще засада… У многих sms-провайдеров цены указаны в районе +-40 копеек. Но как оказалось, это при передаче по так называемым «Международным каналам». На самом деле это интернет-каналы. Потери в таких случаях весьма серьезные (сами провайдеры заявляют до 95% при массовых рассылках). Так что хочешь не хочешь, а надо переключаться на прямой канал. А там цена уже взлетает сразу в 3-4 раза, то есть средняя цена 1.60-1.90 руб за смс. При чем в letsads.com/ информация об этом в личном кабинете выводится очень заметно. А на iqsms.ru/ я этого не увидел нигде. Писал запрос в саппорт. Ответили… joxi.ru/8AnXDv9uqBMpdm

В общем, подводных камней оказалось масса. Здесь прошу всех, у кого есть какой опыт здесь, поделиться, какие sms-провайдеры кто юзает и с какими еще подводными камнями или фишками сталкивался.

Напоследок еще немного о компоненте: Пока код работы с iqsms.ru заморожен за ненадобностью, но в дальнейшем планирую реализацию поддержки множества провайдеров, так что этот провайдер тоже вернется в строй. Пока же основной провайдер — letsads.com/

Компонент логирует отправляемые sms-ки, и потом можно по ним запрашивать статусы. При получении статуса информация обновляется и в БД.

Пока стоимость пакета составляет 1490 рублей и такой останется, пока компонент не получит более оформленный и обширный функционал. Со своей стороны гарантирую информационную и прочую помощь в его использовании. А использовать его можно где угодно, включая уведомления о смене статуса заказов и т.п.

Доступен в modstore.pro.

P.S. Кто хочет опробовать в действии, у нас на сайте если в профиле указать номер телефона, то авторизация становится двухфакторной. Можете посмотреть как это работает.
Fi1osof
30 ноября 2016, 21:35
modx.pro
5
6 606
+12

Комментарии: 43

Василий Столейков
01 декабря 2016, 09:01
1
+1
У многих sms-провайдеров жены указаны в районе +-40 копеек
Дешевые жены у них...
Прости, не удержался — фраза двусмысленной получилась, исправь пожалуйста…

Что включает в себя компонент для работы? Сниппет? Интерфейс в админке?
    Fi1osof
    01 декабря 2016, 09:28
    0
    У многих sms-провайдеров жены указаны в районе +-40 копеек
    Дешевые жены у них...
    Опечатка по Фрейду))

    Что включает в себя компонент для работы? Сниппет? Интерфейс в админке?
    Службу. То есть
    $smsGate = $modx->getService("smsGate");
    $smsGate->send($phone, $message);
    Еще есть таблица в БД, куда складываются отправляемые СМСки и их статусы. Есть метод проверки статуса СМС, при этом ее статус в БД обновляется при запросе.

    Будут продажи и будут пожелания: будут интерфейсы и прочие плюшки.
Павел Гвоздь
01 декабря 2016, 10:28
0
Создайте у себя плагин на событие OnWebAuthentication и пропишите в нем $modx->event->_output = 'false';
False, наверное, без кавычек?

Ночью пытался воспроизвести на чистом MODX 2.5.2 — не удалось.
    Fi1osof
    01 декабря 2016, 10:40
    0
    Именно с кавычками. false без кавычек — это логическое нет, и оно empty(). А вот с кавычками — это строчное 'false' 5 символов, и это не равно empty(). С false без кавычек и не будет работать.
      Павел Гвоздь
      01 декабря 2016, 10:42
      0
      Николай, до меня не доходит. А как туда попадает false в кавычках?
        Fi1osof
        01 декабря 2016, 14:53
        0
        Это просто в плагине прописывается и все. Не обязательно 'false', можно написать 'bla-bla-bla'
        Павел Гвоздь
        01 декабря 2016, 10:54
        0
        Воспроизвести удалось. Но если на то пошло, то имея доступ к запуску процессора и правки плагинов, можно и через MODX API так же авторизовать себя под любым юзером без пароля, в т.ч. и под админом.
          Fi1osof
          01 декабря 2016, 14:55
          0
          Это не стоит расценивать как дыру в безопасности напрямую. Это просто логическая ошибка. И речь не о том, что ей можно воспользоваться для взлома, а о том, что логика не позволяет корректно обрабатывать ошибки. Вот при сохранении документа вы же можете прописать плагин, который в случае чего будет говорить «Документ нельзя сохранить потому-то и потому-то», а вот здесь этого не получается сделать.
      Захарий
      01 декабря 2016, 11:55
      +2
      Использовал на проекте ТЫЦ: цена приятная, подключение проще простого(создание объекта->аутентификация->отправка). Для моих тривиальных целей он подошел более чем. Я не знаю правда систему работы с Россией(это украинский сервис), но вроде как можно.
        Fi1osof
        01 декабря 2016, 14:57
        0
        letsads.com тоже Украинский, и норм все. Гораздо интересней вопрос сколько у них стоит официальный канал, а не интернетовский, но позже посмотрю детальней. Спасибо!
        Павел Голубев
        01 декабря 2016, 13:37
        +1
        smsaero.ru пользуемся 2 года. Отличный саппорт, удобное пополнение счета, личный менеджер для юр. лиц. простой и удобный REST API: статусы доставки, запрос баланса, массовая рассылка по списку контактов и т.д. Полная информация об API здесь smsaero.ru/api/description/. Еще могут отсылать в ВотсАп и Вибер.

        Из минусов:
        Все акции, подарочные сертификаты и т.п. не работают для юр. лиц.
        Ребята сидят в Омске, сдвиг рабочего времени +3 ч от Москвы.
          Fi1osof
          01 декабря 2016, 14:58
          0
          Спасибо! Посмотрю.
          Воеводский Михаил
          01 декабря 2016, 14:19
          +1
          На одном из проектов использую digital-direct.ru. API не простое, а совсем примитивное — один GET запрос с необходимыми параметрами. При этом, насколько помню, есть возможность и статусы проверять, и многие другие вещи выполнять.
            Fi1osof
            01 декабря 2016, 15:02
            0
            Спасибо! Гляну.
              Fi1osof
              01 декабря 2016, 16:09
              0
              Кстати, на счет примитивности: тут API тоже не сложное, но важно было еще сохранять в базе смски. Сейчас все попытки отправки записываются в базу данных и можно позже запросить статус, что довольно удобно. Планирую в ближайшее время сделать еще интерфейс произвольной отправки сообщений прям через управление пользователей (как в switchUser сделано «Авторизоваться», так будет «Отправить сообщение» если номер указан). На отдельных проектах вполне может пригодиться такая функция для оперативного уведомления.
              Fi1osof
              01 декабря 2016, 15:59
              +4
              Все, компонент опубликован. modstore.pro/packages/alerts-mailing/smsgate
                Дмитрий Ломакин
                01 декабря 2016, 23:12
                +1
                Пожалуй не соглашусь с этими претензиями к MODX.

                1. событие «OnWebAuthentication» на самом деле правильнее назвать было бы «OnBeforeWebAuthentication»
                и в этой роли оно свой функционал отрабатывает идеально.

                У меня, например, сейчас с помощью этого события реализован функционал авторизации по ссылке. Пришла ссылка на email — тыц на нее — и ты уже залогинен.

                2. По поводу "$modx->event->_output" вообще претензия странная.
                «Я туда сую неверное значение и MODX плохо работает». Так не суйте неверное.
                Если в процессе работы плагина выходит, что пользователь достоин авторизации, то делаем
                $modx->event->_output = true;
                в иных случаях не трогаем. И все работает как надо.

                3. Добавить событие «OnAfterWebAuthentication» было бы полезно

                  Fi1osof
                  02 декабря 2016, 12:17
                  0
                  2. По поводу "$modx->event->_output" вообще претензия странная.
                  «Я туда сую неверное значение и MODX плохо работает». Так не суйте неверное.
                  Дмитрий, если вам претензия кажется странной, то это просто потому что опыта пока маловато. Вам просто надо поверить мне на слово, что так не должно быть.
                  Но плюс к слову, аргументирую.
                  Вот смотрите, процессор обновления документа расширяет базовый процессор modObjectUpdateProcessor. В этом процессоре есть метод fireBeforeSaveEvent(). Если у текущего процессора есть свойство $this->beforeSaveEvent, то на это событие вызывается плагин. В процессоре обновления документа resource/update это свойство есть, и когда процессор отрабатывается, выполняется и плагин. И в плагине можно вернуть сообщение об ошибке. И на это в базовом процессоре прописан корректный обработчик. Обратите внимание, что в этом обработчике прописано присвоение возвращаемой переменной $preventSave. И внимательно посмотрите на эту конструкцию:
                  $preventSave = $this->fireBeforeSaveEvent();
                          if (!empty($preventSave)) {
                              return $this->failure($preventSave);
                          }
                  Если $preventSave не пустое, то возвращается ошибка. Вот это правильная работа плагина. Создайте плагин на событие OnBeforeDocFormSave и пропишите $modx->event->_output = «false»; и попробуйте сохранить документ. Что получите? Ошибку.


                  Вас смущает 'false'? Так вот, это не неверное значение, как вы выразились, а произвольное строчное значение, то есть сообщение. И это сообщение может быть более непрограммиступонятное, и даже можно подсвечивать произвольные поля.


                  Вот это корректная работа плагина.

                  А что у нас в security/login прописано? Тупо возврат логического false. Из-за чего корректной обработки не происходит.

                  У меня, например, сейчас с помощью этого события реализован функционал авторизации по ссылке. Пришла ссылка на email — тыц на нее — и ты уже залогинен.
                  Вы просто выполняете дополнительные действия, никак не влияя на ход выполнения процессора. А вы попробуйте в этом своем плагине вернуть ошибку пользователю и прервать его авторизацию.

                  Если в процессе работы плагина выходит, что пользователь достоин авторизации, то делаем
                  $modx->event->_output = true;
                  в иных случаях не трогаем. И все работает как надо.
                  У нас с вами разные понятия «как надо». Сделаете вы $modx->event->_output = true; или не сделаете ничего, результат не поменяется в принципе. А вы сделайте так, чтобы поменялся.

                  3. Добавить событие «OnAfterWebAuthentication» было бы полезно
                  А вы поищите хорошенько, может что-то подобное найдете.
                    Дмитрий Ломакин
                    02 декабря 2016, 20:11
                    +1
                    Еще раз:
                    Чушь, что плагин OnAfterWebAuthentication неверно работает и
                    Надеюсь, кто-нибудь отправит багфикс.
                    плагин свою логику отрабатывает на 5+
                    он сейчас работает для обхода проверки пароля
                    для того что бы можно было авторизовать пользователя не зная его пароля
                    ферштейн?

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

                    Про
                    Создайте у себя плагин на событие OnWebAuthentication и пропишите в нем $modx->event->_output = 'false';… Как результат: авторизация. joxi.ru/Y2LjLVESn3pqXr
                    и
                    Сделаете вы $modx->event->_output = true; или не сделаете ничего, результат не поменяется в принципе.
                    Сами себе противоречите )))
                    Если вы не сделаете ничего, то плагина и нет как бы, и тогда с неверным логин/паролем пользователя не авторизуют
                    А если сделаете «modx->event->_output = true;» то проверки пароля не будет
                    и пользователь авторизуется только по логину.

                    Про
                    А вы поищите хорошенько, может что-то подобное найдете.

                    OnWebLogin — не то, в этот момент пользователь уже авторизован, а нужно этого не допустить
                      Fi1osof
                      02 декабря 2016, 21:18
                      0
                      он сейчас работает для обхода проверки пароля
                      для того что бы можно было авторизовать пользователя не зная его пароля
                      ферштейн?
                      Пообщались с Джейсоном И Марком, отправил PR github.com/modxcms/revolution/pull/13204
                      Если примут, то можно будет не только разрешать авторизацию без пароля (если возвращать true), но и можно будет выбрасывать собственные ошибки. Так будет гораздо гораздее.

                      OnWebLogin — не то, в этот момент пользователь уже авторизован, а нужно этого не допустить
                      Как раз этого и хотел не допустить, и это и решает данный PR. Его неидеальность только в том, что придется в нем в случае чего все равно проверять пароль, если надо проверить пароль (так как сейчас проверка пароля идет уже за пределами вызова этого события), но это не страшно. Теперь, если вы считаете, что все проверки пройдены, но не надо позволить пользователю авторизоваться, можно будет просто вернуть сообщение ошибки (не true и не false).
                        Дмитрий Ломакин
                        03 декабря 2016, 01:32
                        +2
                        код бы свой проверили сначала что ли
                        он конечно безобидный, в том смысле что никогда не выполнится )))
                        но полезнее, если работать будет:
                        вместо
                        if ($rt && (is_array($rt) && !in_array(true, $rt)))
                        надо
                        if ($rt && is_array($rt) && !in_array(true, $rt, true))

                        ps Изящно исправили. аж восхитился. Серьезно!
                          Fi1osof
                          03 декабря 2016, 02:05
                          0
                          Спасибо, поправил. Проверял, но видимо к вечеру глаз замылился.
                  Антон
                  Антон
                  02 декабря 2016, 07:52
                  0
                  Какие-то цены зверские на смс.
                  Может есть способ отправлять смс посредством обычной связи.
                  Есть же GSM модули всякие.
                    Fi1osof
                    02 декабря 2016, 12:22
                    0
                    Цифровую подпись тоже обеспечите?


                    Хотя конечно, если где-то что подешевле нароется, будет замечательно.
                    Александр Быковский
                    08 декабря 2016, 17:25
                    0
                    Возможно ли использовать данное решение в купе с AjaxForm + Formit, только вместо оправки формы на e-mail, осуществлялась отправка данных формы на номер администратора сайта по смс?
                      Алексей Ерохин
                      08 декабря 2016, 17:43
                      +1
                      Хук для Formit, там вызываете сервис smsgate и шлете куда угодно.
                      В хуке доступны все поля формы.
                        Fi1osof
                        09 декабря 2016, 06:08
                        0
                        Все верно, все делается через хуки.
                        И как было заявлено в описании компонента, с нашей стороны помощь в настройке. Так что если не справитесь самостоятельно, поможем.
                          Александр Быковский
                          09 декабря 2016, 09:27
                          1
                          0
                          Это было бы здорово, куда пройти за помощью?) Чтобы ветку не захламлять
                            Fi1osof
                            09 декабря 2016, 09:31
                            0
                            Приобретайте пакет в модсторе и пишите там же в поддержку. Я на связи сегодня.
                              Александр Быковский
                              09 декабря 2016, 09:34
                              0
                              Отлично. Дело не сегодняшнего дня, но на следующей неделе обязательно это дело нужно закончить
                                Fi1osof
                                09 декабря 2016, 09:35
                                0
                                Не вопрос.
                                  Александр Быковский
                                  14 декабря 2016, 10:00
                                  0
                                  Николай, еще один «глупый» вопрос: какими сервисами можно пользоваться для отправки смс, важно для определения конечной стоимости для клиента.
                                    Fi1osof
                                    14 декабря 2016, 10:32
                                    0
                                    Про сервисы я писал в статье. Сейчас компонент работает с letsads.com/ (хотя будет позже реализация работы с различными провайдерами, и сразу будет реанимирован iqsms.ru/).
                                    Белые смс-ки на летсадс стоят 1.70. Это средняя по рынку стоимость.
                                      Александр Быковский
                                      14 декабря 2016, 10:43
                                      0
                                      тут тонкий момент, заказчик юридическое лицо и сильно переживает за тонкости юридические при взаиморасчетах с иностранными контрагентами
                                        Fi1osof
                                        14 декабря 2016, 10:47
                                        0
                                        Ну iqsms.ru тогда в помощь. Но там на белом канале по-моему от 5000 платеж минимальный, и смс 1.99 на белом канале.
                                        Этот провайдер сейчас из коробки не поддерживается (просто заморожен), но восстановить не долго, доработаем.
                        Павел
                        12 декабря 2016, 20:12
                        +1
                        А компонент может отправлять смс с нужным мне текстом, а не только паролем? К примеру, остаток на счету
                          Fi1osof
                          13 декабря 2016, 04:40
                          +1
                          Да, как я и писал выше. Просто в любом месте:
                          $smsGate = $modx->getService(«smsGate»);
                          $smsGate->send($phone, $message);
                          Сообщение произвольное, можно даже в кириллице. Но в кириллице одно сообщение — 70 символов, в латинице 140.
                          Vlas
                          13 декабря 2016, 18:13
                          0
                          Кстати не на всех сервисах для массовой смс рассылки цена где-то в пределах 40 копеек, к примеру на этом — https://www.epochta.com.ua/products/sms/ 25 копеек за сообщение, да и если сильно потрудиться, то думаю еще дешевле можно найти
                            Fi1osof
                            14 декабря 2016, 10:37
                            +1
                            Епочта немного висит joxi.ru/DmBXep0uwNJJ9A
                            Повторюсь, гарантии на доставку смсок по таким каналам нет никакой. Нужны белые каналы.

                            UPD: Заработала епочта. Вы немного слукавили со стоимостью. Она зависит от суммы пополнения баланса. Вы готовы за раз пополнить на 75 000 рублей, чтобы по 25 копеек смски слать? Не думаю. А на небольших суммах стоимость не сильно отличается.
                            Размер платежа, руб. Тариф, смс/руб.
                            от 1 до 999 0,51
                            от 1 000 0,48
                            от 2 000 0,43

                            UPD 2: Vlas, регистрация вчера ради одного коммента. Не рекламы ли ради?
                            Sergey
                            25 мая 2018, 13:14
                            0
                            Николай, компонент прекратил свое существование?
                              Fi1osof
                              25 мая 2018, 17:58
                              0
                              Сергей, все компоненты были сняты с публикации modx.pro/news/12550/

                              Но этот вроде как не нуждается в оказании поддержки, недавно ставил, все работает (подключал letsads.com/ ).
                              Выложил пакет в официальный репозиторий, но там еще не опубликовалось. Вот прямая ссылка: modxclub.ru/assets/files/extras/smsgate-1.0.0-beta.transport.zip
                              Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                              43