Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
titleconfig/hupo_configuration.default.yml
password_recovery:
  enabled: falsetrue
  cache:
    expiration: 5 # minutes
  resend_interval: 30 # seconds
  phone_prefix: ""
  recovery_code_length: 4 # minimum 2
  script_path: /etc/hydra/hupo/recovery_script.sh
  max_codes_number: 3

...

Процесс начинается с перехода по ссылке "Забыли пароль?" на форме логина. Ссылка показывается только если в конфиге enabled: true.

Image Added

После перехода по ссылке предлагается ввести логин и телефон для поиска абонента в Гидре.

Image Added

На скриншоте у телефона есть префикс: "+7". Префикс задаётся в конфиге в поле phone_prefix в виде строки. Он будет использоваться для поиска телефона в Гидре, для отправки СМС. 

Логин — логин из доступа к приложению ЛК, телефон — включенный основной телефон для уведомлений, привязанный к БСУ.

Перед поиском из номера телефона удаляются круглые скобки, знак плюса, минусы и пробелы. При нашей конфигурации если абонент ввёл текст "(915) - 777 - 88 - 99", то для поиска используется строка "79157778899". Логин не изменяется.

Если абонент подобрался, то мы переходим к следующему шагу, иначе показывается ошибка: "Абонент с такими данными не существует".

Все коды ошибок заданы в locales/ru.yml, ключ password_recovery.errors.

Генерация кода подтверждения

Длина кода подтверждения задаётся в конфиге в recovery_code_length. Код подтверждения генерируется и хранится в виде строки. Это нужно для того, чтобы работали коды вида "0001".

Затем код и другие данные (дата генерации кода, время хранения записи, логин, телефон, счётчик текущего кода подтверждения) сохраняются в кеш. Если необходимо, такие записи можно найти в кеше по ключу с логином. Формат ключа: "recovery_#{login}". В нашем случае сохранится запись с ключом: "recovery_sir_arthur".

Вызов скрипта для отправки сообщения

Путь к скрипту задаётся в конфиге в script_path. Это должен быть скрипт, который принимает 3 параметра - они описаны в разделе с конфигурационным файлом. Предполагается, что в скрипт обращается к внешнему сервису для отправки СМС сообщения на номер абонента.

После вызова скрипта абонент перенаправляется на страницу с вводом кода подтверждения.

Пример скрипта на Python для сервиса SMS-центр (https://smsc.ru):

Code Block
languagepy
titlesend_sms.py
collapsetrue
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import sys, os, re

login = "SmsCenterLogin"
password = "PassHere"

# Проверка корректности передачи аргументов
if len(sys.argv) < 3:
    os._exit(0)

# Аргументы
phone_number = sys.argv[3]  # Номер телефона
msg = sys.argv[1]           # Сообщение, уже содержит код
code = sys.argv[2]          # Код подтверждения

# Проверка корректности номера телефона
p = re.compile("^\d{11}$")
phone_match = p.match(phone_number)
if not(phone_match) or ((phone_match) and (phone_match.group() != phone_number)):
    os._exit(0)

# Формирование команды с запросом к SMS-центру
cmd = "curl --url \"https://smsc.ru/sys/send.php\" -d " + \
      "\"login=%s&psw=%s&phones=%s&mes=%s&charset=utf-8\"" % \
      (login, password, phone_number, msg)

# Отправка команды через SMS-центр
os.system(cmd)

Вызов скрипта для отправки сообщения внутри docker-контейнера

После переезда HUPO внутрь docker-контейнера чтобы не возиться с pyenv и дополнительными модулями, зачастую проще написать скрипт на bash, если отправка смс сводится к curl запросу. Скрипт должен быть проброшен в docker-compose.yml и быть исполняемым (chmod +x send_sms.sh).

Ниже пример такого скрипта:

Code Block
languagebash
titlesend_sms.sh
collapsetrue
#!/bin/bash
# ВНИМАНИЕ! Проверка отправки смс должна делаться только на определенные номера, которые пропускает СМС-шлюз !
#set -x   - раскомментировать для отладки!
 
# Проверка корректности передачи аргументов
if [ "$#" -ne 3 ]; then
  exit 1
fi

MSG=$1
PIN=$2
PHONE=$3
#PHONE='998933221122' #обрати внимание - номер 12  знакомест, а не 11 как в РФ

DATE=$(date +"%x %T")
LOGFILE=/tmp/password_recovery_hupo.log

PSW='CHANGE_ME'
LGN='CHANGE_ME'
CGPN='CHANGE_ME'
URL='http://sms.com:8088/json2sms' # заменить на свою точку входа

# Проверка корректности номера телефона 
PHONE=`echo "$3" | sed -n '/^[0-9]\{12\}$/p'` #обрати внимание - номер 12  знакомест, а не 11 как в РФ
if [ -z "$PHONE" ]; then
  exit 1
fi

# Формирование команды с запросом к SMS-центру
echo -e | curl --header "Content-Type: application/json" --request POST --data \
"{\"login\":\"$LGN\",\"pwd\":\"$PSW\",\"CgPN\":\"$CGPN\",\"CdPN\":\"$PHONE\",\"text\":\"$MSG\"}" --url $URL -v

echo "$DATE curl --header \"Content-Type: application/json\" --request POST --data \
'{\"login\":\"$LGN\",\"pwd\":\"$PSW\",\"CgPN\":\"$CGPN\",\"CdPN\":\"$PHONE\",\"text\":\"$MSG\"}' --url $URL -v" >> $LOGFILE


Ввод кода подтверждения

Абонент получает код на телефон и вводит его в поле "Код". Если код совпадает с сохраненным в кеше, то абонент переходит на шаг смены пароля. Если код не приходит, то можно отправить повторно с помощью ссылки "Отправить ещё раз". Повторная отправка вызовет генерацию нового кода, вызовет скрипт для отправки СМС и обновит запись в кеше.

Image Added

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

Количество повторных отправок тоже ограничено параметром в конфиге max_codes_number. Если абонент превысит число повторных отправок, то будет выведена ошибка "Превышено число повторных обращений. Обратитесь в контактный центр".

Если абонент ввёл правильный код, то его перебросит на страницу ввода нового пароля.

Ввод нового пароля

Новый пароль нужно вводить дважды. Если пароль в первом инпуте совпадает с паролем во втором инпуте, то пароль меняется.

После сохранения нового пароля запись в кеше удаляется. Запустить восстановление пароля можно ещё раз.

Note

Число таких попыток не ограничено.

После сохранения нового пароля нужно залогиниться в ЛК.

 

Note

Надо понимать, что восстановление пароля и все ограничения (на таймаут, повторную отправку) действуют в течение времени жизни записи в кеше. Это значение задаётся в конфиге в cache.expiration в минутах.