Архив метки: php

В чем опасность использовать прямую работу с БД если есть API

Жил был скрипт, который использовал вот таакой запрос к БД Zabbix:

select sendto from media where userid in (select userid from (select users_groups.userid from operations inner join opmessage_grp on opmessage_grp.operationid=operations.operationid inner join users_groups on opmessage_grp.usrgrpid=users_groups.usrgrpid where operations.actionid in (select actionid from conditions where conditiontype=2 and value=%s union all select actionid from conditions where conditiontype=0 and value in (SELECT groups.groupid FROM triggers INNER JOIN functions ON triggers.triggerid=functions.triggerid INNER JOIN items ON items.itemid=functions.itemid INNER JOIN hosts ON hosts.hostid=items.hostid INNER JOIN hosts_groups ON hosts_groups.hostid=hosts.hostid INNER JOIN groups ON groups.groupid=hosts_groups.groupid WHERE triggers.triggerid=%s) union all select actionid from conditions where conditiontype=1 and value in (SELECT hosts.hostid FROM triggers INNER JOIN functions ON triggers.triggerid=functions.triggerid INNER JOIN items ON items.itemid=functions.itemid INNER JOIN hosts ON hosts.hostid=items.hostid INNER JOIN hosts_groups ON hosts_groups.hostid=hosts.hostid INNER JOIN groups ON groups.groupid=hosts_groups.groupid WHERE triggers.triggerid=%s)) union all select opmessage_usr.userid from operations inner join opmessage_usr on opmessage_usr.operationid=operations.operationid where operations.actionid in (select actionid from conditions where conditiontype=2 and value in (select triggers.triggerid from triggers where triggers.triggerid = %s union all select triggers.templateid from triggers where triggers.triggerid = %s) union all select actionid from conditions where conditiontype=0 and value in (SELECT groups.groupid FROM triggers INNER JOIN functions ON triggers.triggerid=functions.triggerid INNER JOIN items ON items.itemid=functions.itemid INNER JOIN hosts ON hosts.hostid=items.hostid INNER JOIN hosts_groups ON hosts_groups.hostid=hosts.hostid INNER JOIN groups ON groups.groupid=hosts_groups.groupid WHERE triggers.triggerid=%s) union all select actionid from conditions where conditiontype=1 and value in (SELECT hosts.hostid FROM triggers INNER JOIN functions ON triggers.triggerid=functions.triggerid INNER JOIN items ON items.itemid=functions.itemid INNER JOIN hosts ON hosts.hostid=items.hostid INNER JOIN hosts_groups ON hosts_groups.hostid=hosts.hostid INNER JOIN groups ON groups.groupid=hosts_groups.groupid WHERE triggers.triggerid=%s))) as us group by userid union all select userid from users_groups where usrgrpid=18) and mediatypeid=5

Единственной функцией котого было дать список телефонов пользователей по id триггера. И работал бы этот скрипт еще долго, если бы не случилось обновление Zabbix с 3.4 до 4.4 Структура БД изменилась. Запрос работать перестал. Посмотрел я на него, посмотрел.. И понял, я наверное был не в себе когда его писал, и повторить такой подвиг больше не смогу. Решил переписать его использую API Zabbix. Вышло много понятнее 😉

echo "-получаем api-key\n";
$res=$zab->Auth($login, $password);

$triggerid=$options["t"];
$txt=$options["s"];
$users_id_for_sms=array();
echo "-действия по триггеру\n";
$res=$zab->Execute("action.get", ["triggerids"=>$triggerid,"output"=>'extend','selectOperations'=>'extend','selectRecoveryOperations'=>'extend','selectFilter'=>'extend']);
ParseRes($res);
echo "-получаем хост\n";
$hosts=$zab->Execute("host.get", ["triggerids"=>$triggerid]);
foreach ($hosts->result as $host) {
    $hostid=$host->hostid;
    $hostname=$host->name;
    echo "--действия по хосту $hostid $hostname\n";
    $res=$zab->Execute("action.get", ["hostids"=>$hostid,"output"=>'extend','selectOperations'=>'extend','selectRecoveryOperations'=>'extend','selectFilter'=>'extend']);
    ParseRes($res);
    $hostsg=$zab->Execute("hostgroup.get", ["hostids"=>$hostid]);
    foreach ($hostsg->result as $hostg) {
        $groupid=$hostg->groupid;
        echo "--- действия по группе хоста $groupid\n";   
        $res=$zab->Execute("action.get", ["groupids"=>$groupid,"output"=>'extend','selectOperations'=>'extend','selectRecoveryOperations'=>'extend','selectFilter'=>'extend']);
        ParseRes($res);

    }    
};
echo "- пользователи ZABBIX\n";   
var_dump($users_id_for_sms); 
$sms=array();
foreach ($users_id_for_sms as $userid) {    
    $res=$zab->Execute("user.get", ["userids"=>$userid,"output"=>'extend','status'=>0,"selectMedias"=>"extend"]);                     
    foreach ($res->result as $usersinfo) {        
        foreach ($usersinfo->medias as $md) {
            if ($md->mediatypeid==5){
              $sms[]=$md->sendto;  
            };
        };
        
    }
}
var_dump($sms);

function ParseRes($res){
global $zab,$users_id_for_sms;
    if (count($res->result)!=0){
        foreach ($res->result as $oper) {
            foreach ($oper->operations as $vl) {
                echo "---получаю пользователей группы\n";
                foreach ($vl->opmessage_grp as $usergroups) {
                  $usrgrpid= $usergroups->usrgrpid;
                  echo "----$usrgrpid\n";
                  $res=$zab->Execute("user.get", ["usrgrpids"=>$usrgrpid,"output"=>'extend','status'=>0]);                  
                  foreach ($res->result as $user) {
                   $users_id_for_sms[]=$user->userid;
                  };                                                      
//                  $res=$zab->Execute("user.get", ["usrgrpids"=>15,"output"=>'extend','status'=>0]);                  
//                  foreach ($res->result as $user) {
//                   $users_id_for_sms[]=$user->userid;
//                  };                                                                        
                };
                echo "---добавляю просто пользователей\n";                
                 //var_dump($vl->opmessage_usr);
                foreach ($vl->opmessage_usr as $userslist) {
                   $users_id_for_sms[]= $userslist->userid;
                };
                
            };            
        }
    };                             
};

Работа с API Zabbix из PHP

Поискал, поискал, ничего толкового не нашел. Пришлось написать самому. Выложил на GitHub. Описание актуального API Zabbix тут.

Пример использования:

include_once 'zabbix.php';

$url="https://my_zabbix.ru/api_jsonrpc1.php";
$login="weoriufeowiuf";
$password="owerierfoiewroi";

@include_once 'config.php';

// инициализация класса true/false - вывод в режиме debug
$zab=new Tzabbix(false);
$zab->SetCurlOpt(CURLOPT_URL,$url);
echo "-получаем api-key\n";
$res=$zab->Auth($login, $password);
if (isset($res->result)==true){
    if ($res->result!==false){        
        $res=$zab->Execute("host.get", ["output"=>["hostid","host"],"selectInterfaces"=>["interfaceid","ip"]]);
        var_dump($res);
    } else {
        var_dump($res);        
        die(-1);
    };    
} else {
  echo "--не понятная ошибка. попробуйте переключить в debug=true";  
};

Простейшая авторизация на PHP

Иногда проще бывает встроить «базовую» авторизацию в скрипт, чем «пилить» что-то специальное под авторизацию. К счастью протокол HTTP позволяет это сделать — а именно перед открытием страницы можно спросить у пользователя…ну например банальное логин-пароль. Например так:

if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="Enter login and passowrd"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Без авторизации нельзя!';
    exit;
}  else {

if (($_SERVER['PHP_AUTH_USER']=="pavel") and ($_SERVER['PHP_AUTH_PW']=="lkeslek")){

} else {
 die("Логин пароль не верен!");
};

Как сделать простейшее API для своего проекта?

Бывает, что какой-то проект «вырастает из штанишек» и становится необходимым разработать сервис который будет отдавать часть своих данных, по запросу на сторонние сервера. Вот и меня возникла такая необходимость .Вот каркас, с чего начать.

В моем случае сервер защищен от «чужих» протоколом SSL и сертификатом .p12 с паролем. Соответственно для получения данных по API на сторонних ресурсах необходимы будут pem и key файлы от выданного пользователю сертификата.

Для некоторых может быть удобным получение «сессионного ключа» для авторизации и допуска к API.  В принципе доработать не сложно кому нужно. Приведенные ниже примеры — не рабочие, не хватает части файлов, размещено просто для того чтобы можно было понять принцип разработки.

На сервере в корне создадим файл api.php:

<?php

/* 
 * (с) 2011-2019 Грибов Павел
 * http://грибовы.рф * 
 * Если исходный код найден в сети - значит лицензия GPL v.3 * 
 * В противном случае - код собственность ГК Яртелесервис, Мультистрим, Телесервис, Телесервис плюс * 
 */

/* Этот файл - API для получения из NOC различных данных в формате json
 * вход: post/get с заполненым параметром command (массив)
 * выход: json с результатом
 */

$version="1.0.0";


define('WUO_ROOT', dirname(__FILE__));

// Загружаем первоначальные настройки. Если не получилось - выходим
$rez = @include_once(WUO_ROOT.'/config.php');
if ($rez == false) {die();}

header('Content-Type: application/json; charset=utf-8');


// Загружаем классы
include_once(WUO_ROOT.'/class/sql.php'); // Класс работы с БД
include_once(WUO_ROOT.'/class/config.php'); // Класс настроек
include_once(WUO_ROOT.'/inc/connect.php'); // Соединяемся с БД, получаем $mysql_base_id
include_once(WUO_ROOT.'/inc/config.php'); // Подгружаем настройки из БД, получаем заполненый класс $cfg
include_once(WUO_ROOT.'/inc/functions.php'); // Загружаем функции

$command=_GET("command");
if ($command==""){
    $command=_POST("command");
};
if ($command!=""){
	if (is_file(WUO_ROOT."/api/".$command.".php")) {
		include_once(WUO_ROOT."/api/".$command.".php");
	} else {
            $ret["result"]=false;
            $ret["message"]="Метод/команда не найдена";
            die(json_encode($ret));
	}    
} else {
  $ret["result"]=false;
  $ret["message"]="Метод/команда не задан";
  die(json_encode($ret));
};

?>

Как мы видим — суть его, поймать на входе команду, и выполнить файл из папки /api/, совпадающий с именем команды. Так мы избавляемся от головной боли с раширением количества команд API.

В папке /api/  создадим первую команду нашего API — файл version:

<?php

  $ret["result"]=true;
  $ret["message"]="$version";
  die(json_encode($ret));
  
  ?>

Теперь пример использования данного API (для удобства обернуто в класс):

<?php

class NocApi {    
    var $curl_opts = array(
        CURLOPT_URL=>"https://куц.укамцук.ru/api.php",
        CURLOPT_RETURNTRANSFER => true, 
        CURLOPT_SSL_VERIFYPEER => false, // вход по SSL
        CURLOPT_SSL_VERIFYHOST => false,
        CURLOPT_FOLLOWLOCATION => false, // редиректы
        CURLOPT_MAXREDIRS => 10, // максимальное количество редиректов
        CURLOPT_CONNECTTIMEOUT=>5,
        CURLOPT_CONNECTTIMEOUT=>5,
        CURLOPT_VERBOSE=>true,
        CURLOPT_SSLCERT=>"/home/уцм/укм/cert/укмцу.crt",
        CURLOPT_SSLKEY=>"/home/уцкму/цукм/cert/цукм.key",
        CURLOPT_SSLKEYPASSWD=>"123",
        CURLOPT_POST=>true,
        CURLOPT_POSTFIELDS=>"command=version"
    );
    
    public function __construct(){
        if (! function_exists('curl_init')) {
            throw new Exception('CURL модуль для PHP не установлен!');
        }
    }
    public function Exec($runc=""){
        $curl = curl_init();        
        foreach ($this->curl_opts as $opt => $val){
            curl_setopt($curl, $opt, $val);        
        };
        if ($runc!=""){
          curl_setopt($curl, CURLOPT_POSTFIELDS, $runc);  
        };
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
              $ret["result"]=false;
              $ret["message"]=curl_error($curl);
              die(json_encode($ret));            
        } else {
            return json_decode($data);
            curl_close($curl);
        };
    }
    
}

$noc=new NocApi();
$res=$noc->Exec("command=version");
var_dump($res);

Результат выполнения:

object(stdClass)#2 (2) { ["result"]=> bool(true) ["message"]=> string(5) "1.0.0" }

The server requested authentication method unknown to the client

После апгрейда сервера MySQL 8, часть скриптов стала выдавать при коннекте ошибку вида:

The server requested authentication method unknown to the client

Решение: нужно для пользователя поменять способ аутенфикации:

ALTER USER 'vasya'@'localhost' IDENTIFIED WITH mysql_native_password
BY 'password';