Создание дополнительных модулей защиты от мошенничества

Материал из ISPWiki

Перейти к: навигация, поиск

Архитектура BILLmanager позволяет добавлять к системе собственные модули-обработчики. Структура модуля защиты от мошенничества состоит из двух файлов:

  1. XML файл описания интерфейса настройки параметров соединения на стороне провайдера с именем billmgr_mod_xxx.xml, где xxx любая уникальная разрешенная последовательность символов. Файл должен располагаться в папке {путь к папке установки}/etc/ и обязательно должен быть в кодировке UTF-8.
  2. Бинарный или текстовый (в зависимости от языка реализации) файл модуля обработчика. Файл должен располагаться в каталоге /usr/local/ispmgr/sbin/, имя файла должно начинаться с fp.

Содержание

Структура файла XML

Документ XML состоит из двух частей:

  • Описание полей формы параметров
  • Локализованные подписи к полям формы
<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
	<metadata name="fraudparam_xxx" type="form"> <!-- xxx - имя вашего модуля без '''fp''' -->
		<form>
			<!--Поля конфигурации необходимые для работы вашего модуля. Отображаются при вводе параметров модуля-->
			<field name="field_name_1">
				<input type="text" name="field_name_1"/>
			</field>
			<!--...-->
			<field name="field_name_n">
				<input type="text" name="field_name_n"/>
			</field>
		</form>
	</metadata>

	<metadata name="smsgateparam_xxx" type="form"> <!-- xxx - имя вашего модуля без '''fp'''. Раздел нужен, если планируется использовать модуль в качестве СМС шлюза -->
		<form>
			<!--Поля конфигурации необходимые для работы вашего модуля. Отображаются при вводе параметров модуля-->
			<field name="field_name_1">
				<input type="text" name="field_name_1"/>
			</field>
			<!--...-->
			<field name="field_name_n">
				<input type="text" name="field_name_n"/>
			</field>
		</form>
	</metadata>

	<lang name="en"> <!-- В документа XML может быть описано несколько языков, для правильной работы наличие английского языка обязательно -->
		<messages name="fraudconf">
			<msg name="xxx">XXX</msg> <!-- xxx - имя вашего модуля без '''fp''', XXX - отображаемое имя модуля в списке выбора -->
		</messages>

		<messages name="fraudparam_xxx"> <!-- xxx - имя вашего модуля без '''fp''' -->
			<msg name="title">Title</msg> <!-- Заголовок окна ввода параметров -->
			<msg name="field_name_1">field name 1</msg> <!-- Подпись поля в окне настроек модуля -->
			<msg name="hint_field_name_1">hint field name 1</msg> <!-- Подсказка отображаемая при наведении на подпись поля -->
			<!--...-->
			<msg name="field_name_n">field name n</msg>
			<msg name="hint_field_name_n">hint field name n</msg>
		</messages>
		<messages name="smsgateparam_xxx"> <!-- xxx - имя вашего модуля без '''fp'''. Раздел нужен, если планируется использовать модуль в качестве СМС шлюза -->
			<msg name="title">Title</msg> <!-- Заголовок окна ввода параметров -->
			<msg name="field_name_1">field name 1</msg> <!-- Подпись поля в окне настроек модуля -->
			<msg name="hint_field_name_1">hint field name 1</msg> <!-- Подсказка отображаемая при наведении на подпись поля -->
			<!--...-->
			<msg name="field_name_n">field name n</msg>
			<msg name="hint_field_name_n">hint field name n</msg>
		</messages>
	</lang>

	<lang name="ru">
		<!--Те же параметры для другого языка-->
	</lang>
</mgrdata>

Структура модуля

При работе BILLmanager обращается к файлу модуля передавая ему необходимые данные через параметры командной строки. Реализация модуля обработчика должна предусматривать обязательную обработку параметров командной строки и реализовывать соответствующую логику реакции на передаваемые команды.

До версии 4.0.61

Параметры конфигурации модуля сохраняются в файл {путь к папке установки}/etc/fpxxx.conf (sgxxx{ID партнера}.conf - для настроек СМС шлюза), где xxx - имя модуля, в формате: <имя параметра> <значение>. Каждый параметр записан с новой строки.

Начиная с версии 4.0.61

Параметры конфигурации модуля сохраняются в базе данных, таблица support_gate, поле xmldata, в формате XML. Каждый параметр представлен нодой XML документа.

Принимаемые команды и параметры

  • features - в стандартный поток необходимо передать список поддерживаемых функций: fraud и/или gate, например: "fraud gate".
  • validate - необходимо проверить данные и передать 'ok' в стандартный поток вывода или ошибку, если необходимо.
  • tune - при необходимости в ответ на эту команду можно передать в стандартный поток вывода XML документ описания интерфейса.
  • type - необходимо передать в стандартный поток вывода тип модуля: 'sms', если будет производится отправка SMS сообщения, или 'call', если будет производится звонок.
  • sendsms - команда отправки СМС сообщения, принимает параметры: partnerid' phone' и текст сообщения через стандартный поток ввода. Здесь 'partnerid' - идентификатор учетной записи партнера, если отправка СМС производится клиенту партнера, 'phone' - номер телефона, на который производится отправка сообщения. По окончанию отправки в стандартный поток вывода нужно передать "OK", если отправка произведена успешно, и "ERROR", если отправка завершилась с ошибкой.
  • call - вызов обработчика отправки SMS или совершения звонка, полная версия вызова выглядит как fpxxx call ccode phone code lang, где call - управляющая команда, ccode - код страны в номере телефона (первые поле при прохождении процедуры проверки телефона), phone - номер телефона, code - проверочный код, lang - код основного языка пользователя.

По выполнению команды call модуль должен сообщить BILLmanager о своем завершении:

  • В случае успешного завершения необходимо вызвать команду '{путь к папке установки}/sbin/mgrctl -m billmgr -o xml longtask.finish elid=(PID файл запуска) status=ok'
  • В ином случае необходимо вызвать команду '{путь к папке установки}/sbin/mgrctl -m billmgr -o xml longtask.finish elid=(PID файл запуска) status=err errmsg=(текст сообщения об ошибке)'

Подробнее об этом в статье про LongTask

Пример модуля

Рассмотрим создание скрипта модуля на примере подключения к API TextMagic (приведенный ниже текст XML и скрипта обработчика является лишь примером, и не может быть использован в рабочей среде без переработки).

XML документ

Пример XML документа:

<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
	<metadata name="fraudparam_textmagic" type="form">
		<form>

Далее идут параметры конфигурации модуля:

			<field name="username">
				<input type="text" name="username" empty="no"/>
			</field>
			<field name="password">
				<input type="text" name="password" empty="no"/>
			</field>
			<field name="phonereg">
				<input type="text" name="phonereg"/>
			</field>
		</form>
	</metadata>
	<metadata name="smsgateparam_textmagic" type="form">
		<form>

Далее идут параметры конфигурации модуля для настройки СМС шлюза:

			<field name="username">
				<input type="text" name="username" empty="no"/>
			</field>
			<field name="password">
				<input type="text" name="password" empty="no"/>
			</field>
		</form>
	</metadata>

Описание английской локализации формы ввода параметров:

	<lang name="en">
		<messages name="fraudconf">
			<msg name="textmagic">Text Magic</msg>
		</messages>

		<messages name="fraudparam_textmagic">
			<msg name="title">Phone check from Text Magic</msg>
			<msg name="username">User name</msg>
			<msg name="hint_username">Specify a user name</msg>
			<msg name="password">Password</msg>
			<msg name="hint_password">Specify a password</msg>
			<msg name="phonereg">Phone filter</msg>
			<msg name="hint_phonereg">Specify a phone filter. This field can be empty.</msg>
		</messages>
		<messages name="smsgateparam_textmagic">
			<msg name="title">SMS gate from Text Magic</msg>
			<msg name="username">User name</msg>
			<msg name="hint_username">Specify a user name</msg>
			<msg name="password">Password</msg>
			<msg name="hint_password">Specify a password</msg>
		</messages>
	</lang>

Описание русскоязычной локализации формы ввода параметров:

	<lang name="ru">
		<messages name="fraudconf">
			<msg name="textmagic">Text Magic</msg>
		</messages>

		<messages name="fraudparam_textmagic">
			<msg name="title">Проверка телефона от Text Magic</msg>
			<msg name="username">Имя пользователя</msg>
			<msg name="hint_username">Укажите имя пользователя</msg>
			<msg name="password">Пароль</msg>
			<msg name="hint_password">Укажите пароль</msg>
			<msg name="phonereg">Фильтр номеров</msg>
			<msg name="hint_phonereg">Укажите фильтр разрешенных номеров телефонов. Поле можно оставить пустым</msg>
		</messages>
		<messages name="smsgateparam_textmagic">
			<msg name="title">СМС шлюз от Text Magic</msg>
			<msg name="username">Имя пользователя</msg>
			<msg name="hint_username">Укажите имя пользователя</msg>
			<msg name="password">Пароль</msg>
			<msg name="hint_password">Укажите пароль</msg>
		</messages>
	</lang>
</mgrdata>

Скрипт обработчик

Пример скрипта обработчика на PHP

#!/usr/bin/php
<?php

Устанавливаем свой обработчик ошибок для записи их в лог файл вместо передачи в стандартный поток вывода:

	set_error_handler("tmErrorHandler");

Открываем log файл:

	$log_file = fopen("/usr/local/ispmgr/var/fptextmagic.log", "a");
	fwrite($log_file, "=======".date("M j H:i:s") . "[] " ."=======\n");

Записываем принятые параметры в log файл:

	foreach($argv as $line_num => $line) {
		fwrite($log_file, date("M j H:i:s") . "[] " . $line_num.":".$line."\n");
	}

Обработка принятых параметров (первым параметром принимается имя запускаемого файла), если количество принятых параметров меньше двух выводим информацию по использованию модуля, для команды call передается 3 или 4 параметра, значит модуль должен принять 4 или 5 параметров:

	if ($argc<2) {
		usage();
	} else {
		switch($argv[1]) {

Возвращаем 'ok':

			case "features":
				echo "fraud gate";
				break;
			case "validate":
				echo "ok";
				break;
			case "tune":
				break;

Возвращаем тип модуля sms:

			case "type":
				echo "sms";
				break;

Вызываем обработчик отправки SMS:

			case "call":
				if ($argc<5 || $argc>6) {
					fwrite($log_file, date("M j H:i:s") . "[] " . "Invalid params number." . "\n");

					usage();
				} else {
					fwrite($log_file, date("M j H:i:s") . "[] " . "Start call API." . "\n");

					if ($argc == 4) {
						callfraud($argv[2] . $argv[3], $argv[4]);
					} else {
						callfraud($argv[2] . $argv[3], $argv[4], $argv[5]);
					}
				}
				break;
			case "sendsms":
				if ($argc != 3) {
					fwrite($log_file, date("M j H:i:s") . "[] " . "Invalid params number." . "\n");

					usage();
				} else {
					fwrite($log_file, date("M j H:i:s") . "[] " . "Start call API." . "\n");
					$msg;
					$f = fopen('php://stdin', 'r');
					while ($line = fgets($f))
						$msg .= $line;
					callgate($argv[2], $argv[3], $msg);
				}
				break;
			default:
				usage();
		}	
	}

	fwrite($log_file, "=======".date("M j H:i:s") . "[] " ."=======\n");
	fclose($log_file);

Функция вывода информации по использованию модуля:

	function usage() {
		print("ISPsystem BILLmanager plugin v1.0
Text Magic integration plugin.
Usage:
	features - print 'fraud gate';
	validate - print 'ok';
	tune - no return value;
	type - return 'sms' as type of plugin;
	sendsms 'partnerid' 'phone_number' <msg> - send sms to phone number;
	call 'country_code' 'phone_number' 'verification_code' 'lang' - execute plugin function.\n");
}

Функция обработки отправки SMS для защиты от мошенничества:

	function callfraud($phone, $code, $lang = "en") {
		global $log_file;

		fwrite($log_file, date("M j H:i:s") . "[] " . "Phone: " . $phone . " Code: " . $code . "\n");

		$msg = "Your verification code is ".$code;

Считываем параметры из файла конфигурации:

		$lines = file("/usr/local/ispmgr/etc/fptextmagic.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

		$params = array();

		foreach ($lines as $line_num => $line) {
			$arr_key = "";
			$arr_val = "";
			list($arr_key, $arr_val) = explode(" ", $line, 2);
			$params += array($arr_key => $arr_val);
		}

		$curl_client = curl_init("https://www.textmagic.com/app/api");

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

Формируем массив передаваемых на сервер параметров:

		$data = array("username" => $params["username"], "password" => $params["password"], "cmd" => "send",
			      "text" => $msg, "phone" => $phone, "unicode" => "0");

		curl_setopt($curl_client, CURLOPT_SSL_VERIFYPEER, 0);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		curl_setopt($curl_client, CURLOPT_POST, 1);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		curl_setopt($curl_client, CURLOPT_POSTFIELDS, $data);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		$output = curl_exec($curl_client); //В случае успешного завершения запроса в $output помещается 1, в противном случае ничего.

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		fwrite($log_file, date("M j H:i:s") . "[] " . $output . "\n");

Завершаем работу модуля:

		fwrite($log_file, date("M j H:i:s") . "[] Finish task with PID file " . getenv("MGR_LT_PID") . "\n");

		if ($output == "1") {
			exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml longtask.finish elid=". getenv("MGR_LT_PID") ." status=ok");
		} else {
			exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml longtask.finish elid=". getenv("MGR_LT_PID") ." status=err errmsg='Error with cURL'");
		}

		curl_close($curl_client);
	}

Функция отправки СМС в качестве шлюза:

	function callgate($partnerid, $phone, $msg) {
		global $log_file;

		fwrite($log_file, date("M j H:i:s") . "[] " . "PartnerID: " . $partnerid . " Phone: " . $phone . "\n");

Считываем параметры из файла конфигурации:

		$lines = file("/usr/local/ispmgr/etc/sgtextmagic" . ($partnerid > 0 ? $partnerid : "") . ".conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

		$params = array();

		foreach ($lines as $line_num => $line) {
			$arr_key = "";
			$arr_val = "";
			list($arr_key, $arr_val) = explode(" ", $line, 2);
			$params += array($arr_key => $arr_val);
		}

		$curl_client = curl_init("https://www.textmagic.com/app/api");

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

Формируем массив передаваемых на сервер параметров:

		$data = array("username" => $params["username"], "password" => $params["password"], "cmd" => "send",
			      "text" => $msg, "phone" => $phone, "unicode" => "0");

		curl_setopt($curl_client, CURLOPT_SSL_VERIFYPEER, 0);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		curl_setopt($curl_client, CURLOPT_POST, 1);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		curl_setopt($curl_client, CURLOPT_POSTFIELDS, $data);

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		$output = curl_exec($curl_client); //В случае успешного завершения запроса в $output помещается 1, в противном случае ничего.

		fwrite($log_file, date("M j H:i:s") . "[] " . curl_error($curl_client) . "\n");

		fwrite($log_file, date("M j H:i:s") . "[] " . $output . "\n");

		curl_close($curl_client);
	}

Функция обработчика ошибок:

	function tmErrorHandler($errno, $errstr, $errfile, $errline) {
		global $log_file;
		fwrite($log_file, date("M j H:i:s") . "[] " . "Error [" . $errno . "] ErrMsg: " . $errstr . ". In file: " . $errfile . ". In line: " . $errline . "\n");
	    return true;
	}
?>

Пример скрипта обработчика на Python 2.6

Указываем обработчик скрипта и импортируем необходимые модули:

#! /usr/bin/env python
import sys, string, time, httplib, urllib, os, subprocess

Определяем необходимые функции


Определяем функцию вывода в лог файл:

def log(msg):
	log_file.write(time.strftime("%b %d %H:%M:%S[] ") + msg + "\n")

Определяем функцию вывода информации по использованию скрипта:

def usage():
	print "ISPsystem BILLmanager plugin v1.0"
	print "Text Magic integration plugin."
	print "Usage:"
	print "\tvalidate - print 'ok';"
	print "\ttune - no return value;"
	print "\ttype - return 'sms' as type of plugin;"
	print "\tcall 'country_code' 'phone_number' 'verification_code' 'lang' - execute plugin function.\n"

Определяем функцию обработки отправки SMS сообщения:

def callapi(phone, code, lang="en"):
	log("Start call api: phone = " + phone + ", code = " + code + ", lang = " + lang)
	msg = "Your verification code is " + code

Открываем и считываем файл конфигурации:

	config = open("/usr/local/ispmgr/etc/fptextmagic.conf", "r")
	params = {}
	for line in config.readlines():
		line = string.strip(line, " \n")
		kv = line.split(" ")
		try:
			params[kv[0]] = kv[1]
		except IndexError:
			params[kv[0]] = ""
	config.close()

Формируем массив передаваемых на сервер параметров и используя объект класса HTTPSConnection отправляем запрос на сервер:

	params = urllib.urlencode({'username': params["username"], 'password': params["password"], 'cmd': "send", "text": "Your verification code is " + code, 'phone': phone, 'unicode': "0"})
	headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
	http_client = httplib.HTTPSConnection("www.textmagic.com");
	http_client.request("POST", "/app/api", params, headers)
	response = http_client.getresponse()
	data = response.read()
	log(str(response.status) + ":" + str(data))

Завершаем работу модуля, если определена переменная окружения MGR_LT_PID сообщаем BILLmanager о статусе завершения работы скрипта:

	if MGR_LT_PID == "None":
		log("Standalong. No finish action")
	else:
		if response.status == 200:
			subprocess.Popen(["/usr/local/ispmgr/sbin/mgrctl", "-m", "billmgr", "-o", "xml", "longtask.finish", "elid=" + str(os.getenv("MGR_LT_PID")), "status=ok"]);
		else:
			subprocess.Popen(["/usr/local/ispmgr/sbin/mgrctl", "-m", "billmgr", "-o", "xml", "longtask.finish", "elid=" + str(os.getenv("MGR_LT_PID")), "status=err", "errmsg='Error with cURL'"]);

Основной код скрипта


Открываем файл лога для добавления сообщений:

log_file = open("/usr/local/ispmgr/var/fptextmagic.log", "a")

Перенаправляем поток ошибок в файл лога:

sys.stderr = log_file

Считываем переменную окружения содержащую имя PID файла longtask процесса

MGR_LT_PID = str(os.getenv("MGR_LT_PID"))

Обрабатываем принятые параметры командной строки:

if len(sys.argv) < 2:
	log("Too few parameters, print usage")
	usage()
else:

Выводим ответ на команду validate:

	if sys.argv[1] == "validate":
		print "ok"

Выводим ответ на команду tune:

	elif sys.argv[1] == "tune":
		print ""

Выводим ответ на команду type:

	elif sys.argv[1] == "type":
		print "sms"

Обрабатываем команду отправки сообщения (считываем параметры и запускаем функцию отправки):

	elif sys.argv[1] == "call":
		if len(sys.argv) < 5 or len(sys.argv) > 6:
			log("Too few or a lot parameters, print usage")
			usage()
		else:
			phone = sys.argv[2] + sys.argv[3]
			code = sys.argv[4]
			if len(sys.argv) == 5:
				callapi(phone, code)
			elif len(sys.argv) == 6:
				lang = sys.argv[5]
				callapi(phone, code, lang)
			else:
				log("Too few or a lot parameters, print usage")
				usage()
	else:
		usage()

Закрываем указатель на лог файл:

log_file.close()
Была ли эта информация полезной? Да | Нет