PluginProbe
WebTotem Security / 2.4.26
WebTotem Security v2.4.26
3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 2.2.4 All 109 releases
wt-security / lib / API.php

API.php in WebTotem Security 2.4.26, at lib/API.php

1,165 lines 41.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WEBTOTEM_INIT') || WEBTOTEM_INIT !== true) {
4 if (!headers_sent()) {
5 header('HTTP/1.1 403 Forbidden');
6 }
7 die("Protected By WebTotem!");
8 }
9
10 /**
11 * WebTotem API class.
12 *
13 * Mostly contains wrappers for API methods. Check and send methods.
14 *
15 * @version 1.0
16 * @copyright (C) 2022 WebTotem team (http://wtotem.com)
17 * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
18 */
19 class WebTotemAPI extends WebTotem {
20
21 /**
22 * Method for getting an auth token.
23 *
24 * @param string $api_key
25 * Application programming interface key.
26 *
27 * @return bool|string
28 * Returns auth status
29 */
30 public static function auth($api_key) {
31 $domain = WEBTOTEM_SITE_DOMAIN;
32
33 if(substr($api_key, 1, 1) == "-"){
34 $prefix = substr($api_key, 0, 1);
35 if($api_url = self::getApiUrl($prefix)){
36 WebTotemOption::setOptions(['api_url' => $api_url]);
37 } else {
38 WebTotemOption::setNotification('error', __('Invalid API key', 'wtotem'));
39 return FALSE;
40 }
41 $api_key = substr($api_key, 2);
42 }
43
44 if(empty($api_key)) { return FALSE; }
45 $payload = '{"query":"mutation{ guest{ apiKeys{ auth(apiKey:\"' . $api_key . '\", source:\"' . $domain . '\"),{ token{ value, refreshToken, expiresIn } } } } }"}';
46 $result = self::sendRequest($payload, FALSE, TRUE);
47
48 if (isset($result['data']['guest']['apiKeys']['auth']['token']['value'])) {
49 $auth_token = $result['data']['guest']['apiKeys']['auth']['token'];
50 WebTotemOption::login(['token' => $auth_token, 'api_key' => $api_key]);
51 return 'success';
52 } elseif($result['errors'][0]['message'] == 'INVALID_API_KEY') {
53 WebTotemOption::logout();
54 }
55
56 return FALSE;
57 }
58
59 /**
60 * Method for getting API url.
61 *
62 * @param string $prefix
63 *
64 * @return string|bool
65 * API url
66 */
67 public static function getApiUrl($prefix){
68 $urls = [
69 'P' => '.wtotem.com',
70 'C' => '.webtotem.kz',
71 ];
72
73 if(array_key_exists($prefix, $urls)){
74 return 'https://api' . $urls[$prefix] . '/graphql';
75 }
76 return false;
77 }
78
79 /**
80 * Get site info from API server.
81 *
82 * @param string $attempt
83 * Is the request an attempt to get host data.
84 *
85 * @return array
86 * Returns host data.
87 */
88 public static function siteInfo($attempt = FALSE) {
89
90 if(self::isMultiSite()){
91 $host['id'] = WebTotemOption::getSessionOption('host_id');
92
93 if ($host['id']) {
94 return $host;
95 }
96 }
97
98 $host = WebTotemOption::getHost();
99
100 if ($host['id']) {
101 return $host;
102 }
103
104 $all_sites = self::getSites(null, 1000000);
105 if($all_sites){
106 if(self::isMultiSite()) {
107 $sites = get_sites();
108 foreach ($sites as $site){
109 $domain = untrailingslashit($site->domain . $site->path);
110 self::addSite($domain, $all_sites);
111 }
112
113 if (!$attempt) {
114 return self::siteInfo(TRUE);
115 }
116 }
117 else {
118 $domain = WEBTOTEM_SITE_DOMAIN;
119 return self::addSite($domain, $all_sites);
120 }
121 }
122
123 return [];
124 }
125
126 /**
127 * Method for adding a site to the WebTotem platform.
128 *
129 * @param string $domain
130 * Domain to add.
131 * @param array $all_sites
132 * Array with site data on the WebTotem platform.
133 *
134 * @return array
135 * Returns host data.
136 */
137 public static function addSite( $domain, $all_sites) {
138
139 if(function_exists('idn_to_utf8')){
140 $domain = idn_to_utf8($domain);
141 }
142
143 // Checking if the site has been added to the WebTotem.
144 if(array_key_exists('edges', $all_sites)){
145
146 foreach ($all_sites['edges'] as $site){
147 $site = $site['node'];
148 $hostname = untrailingslashit($site['hostname']);
149 // If it added, save site data to DB.
150 if($hostname == $domain or $hostname == 'www.' . $domain){
151 WebTotemOption::setHost($site['hostname'], $site['id']);
152 return [
153 'id' => $site['id'],
154 'name' => $site['hostname'],
155 ];
156 }
157 }
158
159 }
160
161 $scheme = is_ssl() ? 'https' : 'http';
162
163 // If the site is not added then try to add.
164 $payload = '{"variables":{"input":{"title":"' . $domain . '","hostname":"' . $domain . '","configs":{"scheme":"' . $scheme . '","port":' . $_SERVER['SERVER_PORT'] . ',"wa":{},"dec":{},"ps":{}}}},"query":"mutation ($input: CreateSiteInput) { auth { sites { create(input: $input) { id hostname title } } } }"}';
165 $add_site = self::sendRequest($payload, TRUE);
166 if (isset($add_site['errors'])) {
167 WebTotemOption::setNotification('error', __('Failed to add the site to the WebTotem platform.', 'wtotem'));
168 }
169 else {
170 if($host = $add_site['data']['auth']['sites']['create']) {
171 // If it added, save site ID.
172 WebTotemOption::setHost($host['title'], $host['id']);
173 return [
174 'id' => $host['id'],
175 'name' => $host['title'],
176 ];
177 }
178 }
179 return [];
180 }
181
182 /**
183 * Get all sites from API.
184 *
185 * @param string $cursor
186 * Mark for loading data.
187 * @param string $limit
188 * Limit of sites to loading.
189 *
190 * @return array
191 * Returns host data.
192 */
193 public static function getSites($cursor = null, $limit = 15, $filter = false) {
194 $cursor = ($cursor == null) ? 'null' : '\"' . $cursor . '\"';
195 if(!$filter) {
196 $filter = ($limit === 0) ? '' : 'pagination:{ first: ' . $limit . ', cursor: ' . $cursor . ' }';
197 }
198
199 $payload = '{"query":"query getSites { auth { viewer { sites { ...sites } } } } fragment sites on SiteQueries { list(filter: { ' . $filter . ' }) { pageInfo{ hasNextPage endCursor } edges { node { id hostname title createdAt ssl { status } availability { status } reputation { status } ports { status } deface { status } antivirus { status } firewall { status } maliciousScript { stack { name } } } } } }"}';
200 $result = self::sendRequest($payload, true);
201
202 if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
203 return $result['data']['auth']['viewer']['sites']['list'];
204 }
205
206 return [];
207 }
208
209 /**
210 * Method to get the agents file names and AM file link.
211 *
212 * @param string $host_id
213 * Host id on WebTotem.
214 *
215 * @return array
216 * Returns agents files data.
217 */
218 public static function getAgentsFiles($host_id) {
219
220 if(WebTotem::isMultiSite()){
221 $all_hosts = WebTotemOption::getOption('all_hosts');
222 $all_hosts = $all_hosts ? json_decode($all_hosts, true) : [];
223
224 $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
225 $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
226
227 $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
228 $response = self::sendRequest($payload, TRUE);
229
230 if (isset($response['data']['auth']['am']['installMultisite'])) {
231 return $response['data']['auth']['am']['installMultisite'];
232 }
233 }
234 else {
235 $payload = '{"query":"mutation { auth { am { install(siteId: \"' . $host_id . '\"){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
236 $response = self::sendRequest($payload, TRUE);
237 if (isset($response['data']['auth']['am']['install'])) {
238 return $response['data']['auth']['am']['install'];
239 }
240 }
241 return [];
242 }
243
244 /**
245 * Add secondary MultiSite host.
246 *
247 * @param $new_sites
248 * An array with sites to add.
249 *
250 * @return void.
251 */
252 public static function addMultiSiteNewSites($new_sites){
253 // Host id of the main site in MultiSite network.
254 $main_host = WebTotemOption::getMainHost();
255
256 foreach ($new_sites as $site){
257 $all_sites = self::getSites(null, 1000000);
258 $host = self::addSite($site, $all_sites);
259 if(key_exists('id', $host)){
260 $payload = '{"query":"mutation { auth { am { addMultisiteHost(mainSiteId: \"' . $main_host['id'] . '\", siteId: \"' . $host['id'] . '\") } } }"}';
261
262 $result = self::sendRequest($payload, TRUE);
263 if(!$result['errors'][0]['message']){
264 WebTotemOption::setNotification( 'info', __('A new website has been added: ', 'wtotem') . $site);
265 }
266 }
267 }
268 }
269
270 /**
271 * Remove secondary MultiSite host.
272 *
273 * @param $host_id
274 * Host id on WebTotem.
275 *
276 * @return bool
277 * Returns result removing host.
278 */
279 public static function removeMultiSiteHost($host_id){
280 $payload = '{"query":"mutation { auth { am { removeMultisiteHost(siteId: \"' . $host_id . '\") } } }"}';
281 $response = self::sendRequest($payload, TRUE);
282 if (isset($response['data']['auth']['am']['removeSecondaryMultisiteHost'])) {
283 return $response['data']['auth']['am']['removeSecondaryMultisiteHost'];
284 }
285
286 return false;
287 }
288
289 /**
290 * Method to get agents (AM, WAF, AV) statuses.
291 *
292 * @param string $host_id
293 * Host id on WebTotem.
294 *
295 * @return array
296 * Returns agents statuses data.
297 */
298 public static function getAgentsStatusesFromAPI($host_id) {
299 $payload = '{"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { agentManager { statuses { am { status } av { status } waf { status } } } } } } } }", "variables":{"id":"' . $host_id . '"}}';
300 $response = self::sendRequest($payload, TRUE);
301
302 if (isset($response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'])) {
303 return $response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'];
304 }
305
306 return [];
307 }
308
309 /**
310 * Method to get user time zone.
311 *
312 * @return string|bool
313 * Returns time zone data.
314 */
315 public static function getTimeZone() {
316 $payload = '{"query":"query { auth { viewer{ timezone } } } "}';
317 $response = self::sendRequest($payload, TRUE);
318
319 if (isset($response['data']['auth']['viewer']['timezone'])) {
320 return $response['data']['auth']['viewer']['timezone'];
321 }
322 return FALSE;
323 }
324
325 /**
326 * Method for get all the site security data.
327 *
328 * @param string $host_id
329 * Host id on WebTotem.
330 * @param int|array $days
331 * For what period data is needed.
332 *
333 * @return array
334 * Returns all data.
335 */
336 public static function getAllData($host_id, $days = 7) {
337 $language = WebTotem::getLanguage();
338 $period = WebTotem::getPeriod($days);
339
340 $payload = '{"query":"query($id: ID!, $dateRange: DateRangeInput!, $language: Language!, $dateRangeWeek: DateRangeInput!, $wafLogFilter: WafLogFilter!) { auth { viewer { sites { one(id: $id) { openPathSearch { time paths { httpCode severity path } } ports { status lastTest { time } ignorePorts TCPResults{ port technology version cveList{id summary } } UDPResults { port technology version cveList{id summary } } } domain { lastScanResult { isTaken hasSite redirectLink isLocal protection ips { ip location } status time } } sslResults{ results{ certStatus certIssuerName certExpiryDate certIssueDate } } ssl { status daysLeft expiryDate issueDate } reputation { status lastTest { time } virusList { virus{ type path } antiVirus } } firewall { lastTest { time } logs(wafLogFilter: $wafLogFilter){ edges{ node{ type blocked payload ip proxyIp userAgent description source region signatureId location{ country{ nameEn } } time request status country category } } } map(dateRange: $dateRange) { attacks, country } status chart(dateRange: $dateRange) { time attacks blocked } report(dateRange: $dateRange) { time attacks ip } } serverStatus { info { phpVersion phpServerUser phpServerSoftware phpGatewayInterface phpServerProtocol osInfo cpuCount cpuModel CpuFreq cpuFamily lsCpu maxExecTime mathLibraries } ramChart(dateRange: $dateRangeWeek){ total value time } cpuChart(dateRange: $dateRangeWeek){ value time } discUsage{ total free } status } maliciousScript { lastTest { time } status } scoring( language: $language ){ score lastTest{ time } result{ ip country isHigherThan }} agentManager{ createdAt } antivirus { status stats { changed deleted scanned infected error } lastTest { time } isFirstCheck } } } } } }","variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}, "dateRangeWeek":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}, "wafLogFilter": {"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first": 10,"cursor":null}}, "language":"' . $language . '"}}';
341 $response = self::sendRequest($payload, TRUE);
342
343 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
344 return $response['data']['auth']['viewer']['sites']['one'];
345 }
346
347 return [];
348 }
349
350
351 /**
352 * Method for get all the site security data.
353 *
354 * @param string $host_id
355 * Host id on WebTotem.
356 *
357 * @return array
358 * Returns all data.
359 */
360 public static function getMonitoring($host_id) {
361
362 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { domain { lastScanResult { isTaken hasSite redirectLink isLocal protection ips { ip location } status time } } sslResults{ results{ certStatus certIssuerName certExpiryDate certIssueDate } } ssl { status daysLeft expiryDate issueDate } reputation { status lastTest { time } virusList { virus{ type path } antiVirus } } } } } } }","variables":{"id":"' . $host_id . '"}}';
363 $response = self::sendRequest($payload, TRUE);
364
365 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
366 return $response['data']['auth']['viewer']['sites']['one'];
367 }
368
369 return [];
370 }
371
372 /**
373 * Method to get firewall data.
374 *
375 * @param string $host_id
376 * Host id on WebTotem.
377 * @param int $limit
378 * Limit on the number of records.
379 * @param string $cursor
380 * Mark for loading data.
381 * @param int|array $days
382 * For what period data is needed.
383 *
384 * @return array
385 * Returns firewall data.
386 */
387 public static function getFirewall($host_id, $limit = 20, $cursor = NULL, $days = 365) {
388 $period = WebTotem::getPeriod($days);
389 $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
390
391 $payload = '{"query":"query($id: ID!, $wafLogFilter: WafLogFilter!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } chart(dateRange: $dateRange) { time attacks blocked } ...FirewallLogFragment } agentManager { createdAt } } } } } } fragment FirewallLogFragment on Waf { logs(wafLogFilter: $wafLogFilter) { edges { cursor node { type blocked payload ip proxyIp userAgent description source region signatureId location { country { nameEn } } time request status country category } } pageInfo { endCursor hasNextPage } } }", "variables":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"id":"' . $host_id . '","wafLogFilter":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first":' . $limit . ',"cursor":' . $cursor . '}}} }';
392 $response = self::sendRequest($payload, TRUE);
393
394 if (isset($response['data']['auth']['viewer']['sites']['one'])) {
395 return $response['data']['auth']['viewer']['sites']['one'];
396 }
397
398 return [];
399 }
400
401 /**
402 * Method to get firewall chart data.
403 *
404 * @param string $host_id
405 * Host id on WebTotem.
406 * @param int $days
407 * For what period data is needed.
408 *
409 * @return array
410 * Returns firewall chart data.
411 */
412 public static function getFirewallChart($host_id, $days = 7) {
413 $period = WebTotem::getPeriod($days);
414
415 $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { firewall { lastTest { time } status map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } chart(dateRange: $dateRange) { time attacks blocked } } } } } } }", "operationName":null,"variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} } }';
416 $response = self::sendRequest($payload, TRUE);
417
418 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
419 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
420 }
421
422 return [];
423 }
424
425 /**
426 * Method to set firewall settings.
427 *
428 * @param string $host_id
429 * Host id on WebTotem.
430 * @param array $settings
431 * User-specified settings.
432 *
433 * @return array
434 * Returns information whether the request was successful.
435 */
436 public static function setFirewallSettings($host_id, array $settings) {
437 $payload = '{"variables":{"input": {"siteId": "' . $host_id . '", "gdn": ' . $settings['gdn'] . ', "dosProtection": ' . $settings['dosProtection'] . ', "dosLimit": ' . $settings['dosLimit'] . ', "loginAttemptsProtection": ' . $settings['loginAttemptsProtection'] . ', "loginAttemptsLimit": ' . $settings['loginAttemptsLimit'] . '}},"query":"mutation WafSettings($input: WafSettingsInput!) { auth { sites { waf{ settings(input: $input) { gdn dosProtection loginAttemptsProtection dosLimit loginAttemptsLimit } } } } }"}';
438 return self::sendRequest($payload, TRUE);
439 }
440
441 /**
442 * Method to get antivirus data.
443 *
444 * @param array $params
445 * Parameters for filtering data.
446 *
447 * @return array
448 * Returns antivirus data.
449 */
450 public static function getAntivirus(array $params) {
451
452 $cursor = ($params['cursor']) ? '"' . $params['cursor'] . '"' : 'null';
453 $event = ($params['event']) ? '"' . $params['event'] . '"' : '"new"';
454 $permissions = ($params['permissions']) ? ' "permissionsChanged":true, ' : '';
455 $period = WebTotem::getPeriod($params['days']);
456
457 $payload = '{"operationName":null,"variables":{"id":"' . $params['host_id'] . '","avLogFilter":{' . $permissions . '"event":' . $event . ', "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '},"order":{"direction":"DESC","field":"time"},"pagination":{"first":' . $params['limit'] . ',"cursor":' . $cursor . '}}},"query":"query ($id: ID!, $avLogFilter: AvLogFilter!) { auth { viewer { sites { one(id: $id) { id ... on Site { configs { ... on AvConfig { isActive id } } } antivirus { quarantine{ id path date } status log(avLogFilter: $avLogFilter) { edges { node { filePath event signatures time permissions permissionsChanged } } pageInfo { endCursor hasNextPage } } lastTest { time } stats { changed deleted scanned infected } } } } } } }"}';
458 $response = self::sendRequest($payload, TRUE);
459
460 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
461 return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
462 }
463 return [];
464 }
465
466 /**
467 * Method to get antivirus last test.
468 *
469 * @param string $host_id
470 * Host id on WebTotem.
471 *
472 * @return array
473 * Returns antivirus last test data.
474 */
475 public static function getAntivirusLastTest($host_id) {
476
477 $payload = '{"variables":{"id":"' . $host_id . '"},"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { antivirus { status lastTest { time } } } } } } }"}';
478 $response = self::sendRequest($payload, TRUE);
479
480 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
481 return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
482 }
483 return [];
484 }
485
486 /**
487 * Method to force check services.
488 *
489 * @param string $host_id
490 * Host id on WebTotem.
491 * @param string $service
492 * Service that needs to be checked.
493 *
494 * @return array
495 * Returns information whether the request was successful.
496 */
497 public static function forceCheck($host_id, $service) {
498 $payload = '{"variables":{"id":"' . $host_id . '","service":"' . $service . '"},"query":"mutation ($id: ID!, $service: ForceCheckService!) { auth { sites { forceCheck(siteId: $id, service: $service) } } }"} ';
499 return self::sendRequest($payload, TRUE);
500 }
501
502 /**
503 * Method to export antivirus report.
504 *
505 * @param string $host_id
506 * Host id on WebTotem.
507 * @param int|array $days
508 * For what period data is needed.
509 *
510 * @return array
511 * Returns information whether the request was successful.
512 */
513 public static function avExport($host_id, $days = 30) {
514 $period = WebTotem::getPeriod($days);
515 $payload = '{"variables":{ "input":{"siteId":"' . $host_id . '", "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} }},"query":"mutation ($input: AvLogExportInput!) { auth { sites { av { export(input: $input) } } } }"} ';
516 return self::sendRequest($payload, TRUE);
517 }
518
519 /**
520 * Method to get quarantine data.
521 *
522 * @param string $host_id
523 * Host id on WebTotem.
524 *
525 * @return array
526 * Returns quarantine data.
527 */
528 public static function getQuarantineList($host_id) {
529 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ antivirus{ quarantine{ id path date } } } } } } } "}';
530 $response = self::sendRequest($payload, TRUE);
531
532 if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'])) {
533 return $response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'];
534 }
535 return [];
536 }
537
538 /**
539 * Method to move file to quarantine.
540 *
541 * @param string $host_id
542 * Host id on WebTotem.
543 * @param string $path
544 * Path to the file.
545 *
546 * @return array
547 * Returns information whether the request was successful.
548 */
549 public static function moveToQuarantine($host_id, $path) {
550 $payload = '{"query":"mutation{ auth{ sites{ av{ moveToQuarantine(input:{ siteId:\"' . $host_id . '\", path:\"' . $path . '\" }) } } } } "}';
551 return self::sendRequest($payload, TRUE);
552 }
553
554 /**
555 * Method to move file from quarantine.
556 *
557 * @param string $id
558 * Id assigned to the file.
559 *
560 * @return array
561 * Returns information whether the request was successful.
562 */
563 public static function moveFromQuarantine($id) {
564 $payload = '{"query":"mutation{ auth{ sites{ av{ moveFromQuarantine(id: \"' . $id . '\") } } } } "}';
565 return self::sendRequest($payload, TRUE);
566 }
567
568 /**
569 * Method to get server status data.
570 *
571 * @param string $host_id
572 * Host id on WebTotem.
573 * @param int|array $days
574 * For what period data is needed.
575 *
576 * @return array
577 * Returns server status data.
578 */
579 public static function getServerStatusData($host_id, $days = 7) {
580 $period = WebTotem::getPeriod($days);
581 $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { serverStatus { info { phpVersion phpServerUser phpServerSoftware phpGatewayInterface phpServerProtocol osInfo cpuCount cpuModel CpuFreq cpuFamily lsCpu maxExecTime mathLibraries } ramChart(dateRange: $dateRange){ total value time } cpuChart(dateRange: $dateRange){ value time } } } } } } }", "variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} } }';
582
583 $response = self::sendRequest($payload, TRUE);
584
585 if (isset($response['data']['auth']['viewer']['sites']['one']['serverStatus'])) {
586 return $response['data']['auth']['viewer']['sites']['one']['serverStatus'];
587 }
588
589 return [];
590 }
591
592 /**
593 * Method to remove port from ignore list.
594 *
595 * @param string $host_id
596 * Host id on WebTotem.
597 * @param string $port
598 * User specified port.
599 *
600 * @return array
601 * Returns information whether the request was successful.
602 */
603 public static function removeIgnorePort($host_id, $port) {
604 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . $port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { removeIgnorePort(input: $input) } } } }"} ';
605 return self::sendRequest($payload, TRUE);
606 }
607
608 /**
609 * Method to add port to ignore list.
610 *
611 * @param string $host_id
612 * Host id on WebTotem.
613 * @param string $port
614 * User specified port.
615 *
616 * @return array
617 * Returns information whether the request was successful.
618 */
619 public static function addIgnorePort($host_id, $port) {
620 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . (int) $port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { addIgnorePort(input: $input) } } } }"} ';
621 return self::sendRequest($payload, TRUE);
622 }
623
624 /**
625 * Method to get all ports list.
626 *
627 * @param string $host_id
628 * Host id on WebTotem.
629 *
630 * @return array
631 * Returns ports data.
632 */
633 public static function getAllPortsList($host_id) {
634 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { ports { status lastTest { time } ignorePorts TCPResults{ port technology version cveList{id summary } } UDPResults { port technology version cveList{id summary } } } } } } } } ","variables":{"id":"' . $host_id . '"}}';
635
636 $response = self::sendRequest($payload, TRUE);
637
638 if (isset($response['data']['auth']['viewer']['sites']['one']['ports'])) {
639 return $response['data']['auth']['viewer']['sites']['one']['ports'];
640 }
641
642 return [];
643 }
644
645 /**
646 * Method to get all ports list.
647 *
648 * @param string $host_id
649 * Host id on WebTotem.
650 *
651 * @return array
652 * Returns ports data.
653 */
654 public static function getOpenPaths($host_id) {
655 $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { openPathSearch { time paths { httpCode severity path } } } } } } } ","variables":{"id":"' . $host_id . '"}}';
656
657 $response = self::sendRequest($payload, TRUE);
658
659 if (isset($response['data']['auth']['viewer']['sites']['one']['openPathSearch'])) {
660 return $response['data']['auth']['viewer']['sites']['one']['openPathSearch'];
661 }
662
663 return [];
664 }
665
666 /**
667 * Method to get all reports.
668 *
669 * @param string $host_id
670 * Host id on WebTotem.
671 * @param int $limit
672 * Limit on the number of records.
673 * @param string $cursor
674 * Mark for loading data.
675 *
676 * @return array
677 * Returns reports data.
678 */
679 public static function getAllReports($host_id, $limit = 10, $cursor = NULL) {
680 $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
681 $payload = '{"variables":{"filter": { "order": { "direction": "DESC", "field": "created_at"}, "siteId":"' . $host_id . '", "pagination":{"first":' . $limit . ', "cursor":' . $cursor . '} } },"query":"query ReportsQuery($filter: ReportListFilter!) { auth { viewer { reports { list(filter: $filter) { edges { node { id site { hostname } createdAt wa dc ps rc sc av waf } cursor } pageInfo { endCursor hasNextPage } } } } } }"}';
682 $response = self::sendRequest($payload, TRUE);
683
684 if (isset($response['data']['auth']['viewer']['reports']['list']['edges'])) {
685 return $response['data']['auth']['viewer']['reports']['list'];
686 }
687
688 return [];
689 }
690
691 /**
692 * Method to generate report.
693 *
694 * @param string $host_id
695 * Host id on WebTotem.
696 * @param int|array $days
697 * For what period data is needed.
698 * @param array $services
699 * User-specified module settings.
700 *
701 * @return string|bool
702 * Returns report download link.
703 */
704 public static function generateReport(string $host_id, $days, array $services) {
705 $period = WebTotem::getPeriod($days);
706 $language = WebTotem::getLanguage();
707
708 $payload = '{"query":"query ($input: GenerateReportInput) { auth { viewer { reports { generate(input: $input) } } } }", "variables":{ "input": { "siteId": "' . $host_id . '", "from": ' . $period['from'] . ', "to": ' . $period['to'] . ', "wa": ' . $services['wa'] . ', "dc": ' . $services['dc'] . ', "ps": ' . $services['ps'] . ', "rc": ' . $services['rc'] . ', "sc": ' . $services['sc'] . ', "av": ' . $services['av'] . ', "waf": ' . $services['waf'] . ', "language": "' . $language . '" } } }';
709 $response = self::sendRequest($payload, TRUE);
710
711 if (isset($response['data']['auth']['viewer']['reports']['generate'])) {
712 return $response['data']['auth']['viewer']['reports']['generate'];
713 }
714
715 return FALSE;
716 }
717
718 /**
719 * Method to download report.
720 *
721 * @param string $id
722 * Assigned to the report.
723 *
724 * @return string|bool
725 * Returns report download link.
726 */
727 public static function downloadReport($id) {
728 $payload = '{"query": "query { auth { viewer { reports { download(id: \"' . $id . '\") } } } }"}';
729 $response = self::sendRequest($payload, TRUE);
730
731 if (isset($response['data']['auth']['viewer']['reports']['download'])) {
732 return $response['data']['auth']['viewer']['reports']['download'];
733 }
734
735 return FALSE;
736 }
737
738 /**
739 * Method to get configs data.
740 *
741 * @param string $host_id
742 * Host id on WebTotem.
743 *
744 * @return array|bool
745 * Returns configs data.
746 */
747 public static function getConfigs($host_id) {
748 $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ configs{ ... on WaConfig { id service isActive notifications } ... on WafConfig { id service isActive notifications } ... on AvConfig { id service isActive notifications } ... on DcConfig { id service isActive notifications } ... on DecConfig { id service isActive } ... on RcConfig { id service isActive notifications} ... on CmsConfig { id service isActive } ... on PsConfig { id service isActive notifications } ... on SsConfig { id service isActive } ... on ScConfig { id service isActive } } } } } } } "}';
749 $response = self::sendRequest($payload, TRUE);
750
751 if (isset($response['data']['auth']['viewer']['sites']['one']['configs'])) {
752 return $response['data']['auth']['viewer']['sites']['one']['configs'];
753 }
754
755 return FALSE;
756 }
757
758 /**
759 * Method to toggle modules config.
760 *
761 * @param string $service_id
762 * Service id that we enable or disable.
763 *
764 * @return string|bool
765 * Returns information whether the request was successful.
766 */
767 public static function toggleConfigs($service_id) {
768 $payload = '{"query":"mutation{ auth{ configs{ toggle(id: \"' . $service_id . '\"){ ... on WaConfig { service isActive } ... on AvConfig { service isActive } ... on DcConfig { service isActive } ... on DecConfig { service isActive } ... on RcConfig { service isActive } ... on CmsConfig { service isActive } ... on PsConfig { service isActive } ... on WafConfig { service isActive } } } } } "}';
769 $response = self::sendRequest($payload, TRUE);
770
771 if (isset($response['data']['auth']['configs']['toggle'])) {
772 return $response['data']['auth']['configs']['toggle'];
773 }
774
775 return FALSE;
776 }
777
778 /**
779 * Method to toggle modules notification.
780 *
781 * @param string $host_id
782 * Host id on WebTotem.
783 * @param string $service
784 * Service id in which we enable or disable notifications.
785 *
786 * @return string|bool
787 * Returns information whether the request was successful.
788 */
789 public static function toggleNotifications($host_id, $service) {
790 $payload = '{"query":"mutation{ auth{ sites{ toggleNotifications(siteId: \"' . $host_id . '\", service: ' . $service . ') } } }"}';
791 $response = self::sendRequest($payload, TRUE);
792
793 if (isset($response['data']['auth']['sites']['toggleNotifications'])) {
794 return $response;//['data']['auth']['sites']['toggleNotifications'];
795 }
796
797 return FALSE;
798 }
799
800 /**
801 * Method to get allow/deny ip list.
802 *
803 * @param string $host_id
804 * Host id on WebTotem.
805 *
806 * @return array|bool
807 * Returns ip allow/deny lists.
808 */
809 public static function getIpLists($host_id) {
810 $payload = '{"variables":{ "id": "' . $host_id . '" },"query":"query($id: ID!) { auth { viewer { sites{ one(id: $id){ firewall{ blackList{ id ip createdAt } whiteList{ id ip createdAt } settings{ gdn dosProtection dosLimit loginAttemptsProtection loginAttemptsLimit } } } } } } }"} ';
811 $response = self::sendRequest($payload, TRUE);
812
813 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
814 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
815 }
816
817 return [];
818 }
819
820 /**
821 * Method to add ip to allow/deny list.
822 *
823 * @param string $host_id
824 * Host id on WebTotem.
825 * @param string $ips
826 * Ip address list.
827 * @param string $list
828 * Allow or deny list.
829 *
830 * @return bool
831 * Returns information whether the request was successful.
832 */
833 public static function addIpToList($host_id, $ips, $list) {
834
835 if ($ips) {
836 $ips = WebTotem::convertIpListForApi($ips);
837 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "ips": ' . $ips . ', "color": "' . $list . '" } }, "query":"mutation($input: WafListInput!) { auth { sites { waf { addToList(input: $input){ status invalidIPs} } } } }"} ';
838 $response = self::sendRequest($payload, TRUE);
839
840 if (isset($response['data']['auth']['sites']['waf']['addToList'])) {
841 return $response['data']['auth']['sites']['waf']['addToList'];
842 }
843 }
844
845 return FALSE;
846 }
847
848 /**
849 * Method to remove ip from allow/deny list by id.
850 *
851 * @param string $id
852 * Id assignment to ip address.
853 *
854 * @return bool
855 * Returns information whether the request was successful.
856 */
857 public static function removeIpFromList($id) {
858 $payload = '{"variables":{ "id": "' . $id . '" },"query":"mutation($id: ID!) { auth { sites { waf { removeFromList(id: $id) } } } }"} ';
859 $response = self::sendRequest($payload, TRUE);
860
861 if (isset($response['data']['auth']['sites']['waf']['removeFromList'])) {
862 return $response['data']['auth']['sites']['waf']['removeFromList'];
863 }
864
865 return FALSE;
866 }
867
868 /**
869 * Method to get allow url list.
870 *
871 * @param string $host_id
872 * Host id on WebTotem.
873 *
874 * @return array
875 * Returns url allow lists.
876 */
877 public static function getAllowUrlList($host_id) {
878 $payload = '{"query":"query { auth { viewer { sites { one(id: \"' . $host_id . '\"){ firewall{ urlWhiteList{ id url createdAt } } } } } } }"} ';
879 $response = self::sendRequest($payload, TRUE);
880
881 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'])) {
882 return $response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'];
883 }
884
885 return [];
886 }
887
888 /**
889 * Method to add url to allow list.
890 *
891 * @param string $host_id
892 * Host id on WebTotem.
893 * @param string $url
894 * User-specified url.
895 *
896 * @return bool|string
897 * Returns information whether the request was successful.
898 */
899 public static function addUrlToAllowList($host_id, $url) {
900 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "url": "' . $url . '" } }, "query":"mutation($input: WafUrlWhiteListInput!) { auth { sites { waf { addToUrlWhiteList(input: $input) } } } }"} ';
901 $response = self::sendRequest($payload, TRUE);
902
903 if (isset($response['data']['auth']['sites']['waf']['addToUrlWhiteList'])) {
904 return $response['data']['auth']['sites']['waf']['addToUrlWhiteList'];
905 }
906
907 return FALSE;
908 }
909
910 /**
911 * Method to remove url from allow list.
912 *
913 * @param string $id
914 * Id assignment to url address.
915 *
916 * @return bool|string
917 * Returns information whether the request was successful.
918 */
919 public static function removeUrlFromAllowList($id) {
920 $payload = '{"variables":{ "id": "' . $id . '" }, "query":"mutation($id: ID!) { auth { sites { waf { removeFromUrlWhiteList(id: $id) } } } }"} ';
921 $response = self::sendRequest($payload, TRUE);
922
923 if (isset($response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'])) {
924 return $response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'];
925 }
926
927 return FALSE;
928 }
929
930 /**
931 * Method to get blocked countries list.
932 *
933 * @param string $host_id
934 * Host id on WebTotem.
935 *
936 * @return array
937 * Returns blocked countries list.
938 */
939 public static function getBlockedCountries($host_id) {
940 $period = WebTotem::getPeriod(7);
941 $payload = '{"variables":{"dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '}} , "query":"query($dateRange: DateRangeInput!){ auth { viewer { sites { one(id: \"' . $host_id . '\"){ firewall{ blockedCountries map(dateRange: $dateRange) { attacks, country, location { country { nameEn } } } } } } } } }"}';
942 $response = self::sendRequest($payload, TRUE);
943
944 if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
945 return $response['data']['auth']['viewer']['sites']['one']['firewall'];
946 }
947
948 return [];
949 }
950
951 /**
952 * Method for synchronizing data on the list of blocked countries.
953 *
954 * @param string $host_id
955 * Host id on WebTotem.
956 * @param array $countries
957 * Array of countries to block.
958 *
959 * @return bool|string
960 * Returns information whether the request was successful.
961 */
962 public static function syncBlockedCountries($host_id, $countries) {
963
964 $countries = $countries ? WebTotem::convertArrayToString($countries) : '';
965 $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "countries": [' . $countries . '] } }, "query":"mutation($input: WafBlockedCountriesInput!) { auth { sites { waf { syncBlockedCountries(input: $input) } } } }"} ';
966 $response = self::sendRequest($payload, TRUE);
967
968 if (isset($response['data']['auth']['sites']['waf']['syncBlockedCountries'])) {
969 return $response['data']['auth']['sites']['waf']['syncBlockedCountries'];
970 }
971
972 return FALSE;
973 }
974
975 /**
976 * Method to get user's email.
977 *
978 * @return string
979 * Returns user's email.
980 */
981 public static function getEmail(){
982 $payload = '{"query":"query { auth { viewer { email } } }"}';
983 $response = self::sendRequest($payload, true);
984
985 return $response['data']['auth']['viewer']['email'];
986 }
987
988 /**
989 * Method to get user's feedback.
990 *
991 * @return array
992 */
993 public static function getFeedback(){
994 return self::sendFeedbackRequest("GET");
995 }
996
997 /**
998 * Method to set user's feedback.
999 *
1000 * @return array
1001 */
1002 public static function setFeedback($data){
1003 return self::sendFeedbackRequest("POST", $data);
1004 }
1005
1006 /**
1007 * Function sends data request to endpoint.
1008 *
1009 * @param array $data
1010 * Data array to be sent to endpoint.
1011 *
1012 * @return array
1013 * Returns response from WebTotem endpoint.
1014 */
1015 protected static function sendFeedbackRequest($method, $data = []) {
1016 $url = 'https://nps.wtotem.com/user-score';
1017 $email = WebTotem::getUserEmail();
1018
1019 if(!$email){
1020 WebTotemOption::setNotification('error', __( 'First you need to log in', 'wtotem' ));
1021 return [];
1022 }
1023
1024 if($method == "GET"){
1025
1026 $args = [
1027 'timeout' => '30',
1028 'sslverify' => FALSE,
1029 ];
1030
1031 $response = wp_remote_get($url . '?email=' . urlencode($email), $args);
1032
1033 } else {
1034 $data['email'] = $email;
1035 $data['platform'] = 'WORDPRESS';
1036 $data = json_encode($data);
1037
1038 $args = [
1039 'body' => $data,
1040 'timeout' => '30',
1041 'sslverify' => FALSE,
1042 'headers' => [
1043 'Content-Type' => 'application/json',
1044 ],
1045 ];
1046
1047 $response = wp_remote_post($url, $args);
1048 }
1049
1050
1051
1052 $http_code = wp_remote_retrieve_response_code($response);
1053
1054 if ($http_code < 200) {
1055 WebTotemOption::setNotification('error', __( 'Could not connect to feedback endpoint.', 'wtotem' ));
1056 return [];
1057 }
1058
1059 $response_body = wp_remote_retrieve_body($response);
1060 return json_decode($response_body, true);
1061 }
1062
1063 /**
1064 * Function sends GraphQL request to API server.
1065 *
1066 * @param string $payload
1067 * Payload to be sent to API server.
1068 * @param bool $token
1069 * Whether a token is needed when sending a request.
1070 * @param bool $repeat
1071 * Required to avoid recursion.
1072 *
1073 * @return array
1074 * Returns response from WebTotem API.
1075 */
1076 protected static function sendRequest($payload, $token = FALSE, $repeat = FALSE) {
1077
1078 $api_key = WebTotemOption::getOption('api_key');
1079
1080 // Remote URL where the public WebTotem API service is running.
1081 $api_url = WebTotemOption::getOption('api_url');
1082 if(!$api_url){
1083 $api_url = self::getApiUrl('P');
1084 WebTotemOption::setOptions(['api_url' => $api_url]);
1085 }
1086
1087 // Checking whether a token is needed.
1088 if ($token) {
1089 $auth_token = WebTotemOption::getOption('auth_token');
1090 $auth_token_expired = WebTotemOption::getOption('auth_token_expired');
1091
1092 // Checking whether the token has expired.
1093 if ($auth_token_expired <= time() && !$repeat) {
1094 $result = self::auth($api_key);
1095 if ($result === 'success') {
1096 return self::sendRequest($payload, $token, TRUE);
1097 }
1098 else {
1099 if(isset($result['errors'])){
1100 $message = WebTotem::messageForHuman($result['errors'][0]['message']);
1101 WebTotemOption::setNotification('error', $message);
1102 }
1103 }
1104 }
1105 }
1106
1107 if (function_exists('wp_remote_post')) {
1108
1109 $args = [
1110 'body' => $payload,
1111 'timeout' => '60',
1112 'sslverify' => false,
1113 'headers' => [
1114 'Content-Type:application/json',
1115 'Content-Type' => 'application/json',
1116 'Accept: application/json',
1117 'source: WORDPRESS',
1118 ],
1119 ];
1120
1121 if (isset($auth_token)) {
1122 $auth = "Bearer " . $auth_token;
1123 $args['headers'] = array_merge($args['headers'], ["Authorization" => $auth]);
1124 }
1125
1126 $response = wp_remote_post($api_url, $args);
1127 $response = wp_remote_retrieve_body($response);
1128 $response = json_decode($response, true);
1129
1130 }
1131 else {
1132 $error = 'WP_REMOTE_POST_NOT_EXIST';
1133 }
1134
1135 // Checking if there are errors in the response.
1136 if (isset($response['errors'][0]['message'])) {
1137 $message = WebTotem::messageForHuman($response['errors'][0]['message']);
1138 if (stripos($response['errors'][0]['message'], "INVALID_TOKEN") !== FALSE && !$repeat) {
1139 $response = self::auth($api_key);
1140 if ($response === 'success') {
1141 return self::sendRequest($payload, $token, TRUE);
1142 }
1143 }
1144 elseif(stripos($response['errors'][0]['message'], "USERHOST_NOT_BELONG_TO_USER") !== FALSE){
1145 if(WebTotem::isMultiSite()){
1146 WebTotemOption::clearAllHosts();
1147 WebTotemOption::clearOptions([ 'host_id', 'host_name' ]);
1148 } else {
1149 WebTotemOption::clearOptions([ 'host_id', 'host_name' ]);
1150 }
1151 }
1152 else {
1153 WebTotemOption::setNotification('error', $message);
1154 }
1155 }
1156
1157 if (!empty($error)) {
1158 WebTotemOption::setNotification('error', $error);
1159 }
1160
1161 return $response;
1162 }
1163
1164 }
1165