PluginProbe
WebTotem Security / 2.4.31
WebTotem Security v2.4.31
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
← All changes | lib/API.php +317 -345 trunk2.4.31 View file →
@@ -27,42 +27,43 @@
27 27 *
28 28 * @return bool|string
29 29 * Returns auth status
30 30 */
31 - public static function auth($api_key, $repeat = FALSE)
31 + public static function auth($api_key)
32 32 {
33 33 $domain = WEBTOTEM_SITE_DOMAIN;
34 34
35 + if (substr($api_key, 1, 1) == "-") {
36 + $prefix = substr($api_key, 0, 1);
37 + if ($api_url = self::getApiUrl($prefix)) {
38 + WebTotemOption::setOptions(['api_url' => $api_url]);
39 + } else {
40 + WebTotemOption::setNotification('error', __('Invalid API key', 'wtotem'));
41 + return FALSE;
42 + }
43 + $api_key = substr($api_key, 2);
44 + }
45 +
35 46 if (empty($api_key)) {
36 47 return FALSE;
37 48 }
49 + $payload = '{"query":"mutation{ guest{ apiKeys{ auth(apiKey:\"' . $api_key . '\", source:\"' . $domain . '\"),{ token{ value, refreshToken, expiresIn } } } } }"}';
50 + $result = self::sendRequest($payload, FALSE, TRUE);
38 51
39 - $data = ['api_key' => $api_key, 'site' => $domain];
40 - $result = self::sendRequest('auth/sign-in/api-key', $data, 'POST', FALSE, TRUE);
41 -
42 - if($result === null){
43 - WebTotemOption::setNotification('warning' , __('Authorization failed. The server may be temporarily unavailable', 'wtotem'));
44 - }
45 -
46 - if (isset($result['access_token'])) {
47 - $auth_token = $result['access_token'];
52 + if (isset($result['data']['guest']['apiKeys']['auth']['token']['value'])) {
53 + $auth_token = $result['data']['guest']['apiKeys']['auth']['token'];
48 54 if(!WebTotemOption::isActivated()){
49 55 WebTotemOption::login(['token' => $auth_token, 'api_key' => $api_key]);
50 56 WebTotemAgentManager::postdelete();
51 57 } else {
52 - WebTotemOption::refreshToken($auth_token);
58 + WebTotemOption::setOptions(['auth_token' => $auth_token['value'], 'auth_token_expired' => time() + $auth_token['expiresIn'] - 60]);
53 59 }
54 60
55 61 return 'success';
56 - } elseif (isset($result['message']) and $result['message'] == 'invalid credentials') {
62 + } elseif (isset($result['errors'][0]['message']) and $result['errors'][0]['message'] == 'INVALID_API_KEY') {
57 63 WebTotemOption::logout();
58 64 }
59 65
60 - if($repeat == false){
61 - //self::checkEndpoint();
62 - return self::auth($api_key, true);
63 - }
64 -
65 66 return FALSE;
66 67 }
67 68
68 69 /**
@@ -67,17 +68,26 @@
67 68
68 69 /**
69 70 * Method for getting API url.
70 71 *
72 + * @param string $prefix
73 + *
71 74 * @return string|bool
72 75 * API url
73 76 */
74 - public static function getApiUrl()
77 + public static function getApiUrl($prefix)
75 78 {
76 - return 'https://app.wtotem.com';
79 + $urls = [
80 + 'P' => '.wtotem.com',
81 + 'C' => '.webtotem.kz',
82 + ];
83 +
84 + if (array_key_exists($prefix, $urls)) {
85 + return 'https://api' . $urls[$prefix] . '/graphql';
86 + }
87 + return false;
77 88 }
78 89
79 -
80 90 /**
81 91 * Get site info from API server.
82 92 *
83 93 * @param string $attempt
@@ -89,9 +99,8 @@
89 99 public static function siteInfo($attempt = FALSE)
90 100 {
91 101 if (self::isMultiSite()) {
92 102 $host['id'] = WebTotemOption::getSessionOption('host_id');
93 - $host['name'] = WebTotemOption::getSessionOption('host_name');
94 103
95 104 if ($host['id']) {
96 105 return $host;
97 106 }
@@ -102,26 +111,25 @@
102 111 if ($host['id']) {
103 112 return $host;
104 113 }
105 114
106 -// if (self::isMultiSite()) {
107 -// $sites = get_sites();
108 -// foreach ($sites as $site) {
109 -// $domain = untrailingslashit($site->domain . $site->path);
110 -// self::addSite($domain);
111 -// }
112 -//
113 -// if (!$attempt) {
114 -// return self::siteInfo(TRUE);
115 -// }
116 -// } else {
117 -// $domain = WEBTOTEM_SITE_DOMAIN;
118 -// return self::addSite($domain);
119 -// }
120 - $domain = WEBTOTEM_SITE_DOMAIN;
121 - return self::addSite($domain);
115 + if (self::isMultiSite()) {
116 + $sites = get_sites();
117 + foreach ($sites as $site) {
118 + $domain = untrailingslashit($site->domain . $site->path);
119 + self::addSite($domain);
120 + }
122 121
123 -// return [];
122 + if (!$attempt) {
123 + return self::siteInfo(TRUE);
124 + }
125 + } else {
126 + $domain = WEBTOTEM_SITE_DOMAIN;
127 + return self::addSite($domain);
128 + }
129 +
130 +
131 + return [];
124 132 }
125 133
126 134 /**
127 135 * Method for adding a site to the WebTotem platform.
@@ -137,34 +145,42 @@
137 145 if (function_exists('idn_to_utf8')) {
138 146 $domain = idn_to_utf8($domain);
139 147 }
140 148
149 + $matches = self::checkForMatches($domain);
150 +
141 151 // Checking if the site has been added to the WebTotem.
142 - if(!$host = self::getHostID($domain)){
143 - $host = self::getHostID('www.' . $domain);
152 + if (array_key_exists('edges', $matches)) {
153 +
154 + foreach ($matches['edges'] as $site) {
155 + $site = $site['node'];
156 + $hostname = untrailingslashit($site['hostname']);
157 + // If it added, save site data to DB.
158 + if ($hostname == $domain or $hostname == 'www.' . $domain) {
159 + WebTotemOption::setHost($site['hostname'], $site['id']);
160 + return [
161 + 'id' => $site['id'],
162 + 'name' => $site['hostname'],
163 + ];
164 + }
165 + }
166 +
144 167 }
145 168
146 - if($host['id']){
147 - return [
148 - 'id' => $host['id'],
149 - 'name' => $host['hostname'],
150 - ];
151 - }
152 -
169 + $scheme = is_ssl() ? 'https' : 'http';
170 +
153 171 // If the site is not added then try to add.
154 - $data = ['hosts' => [$domain]];
155 - $response = self::sendRequest('hosts', $data, 'POST', TRUE);
156 -
157 - if (isset($response['message'])) {
172 + $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 } } } }"}';
173 + $add_site = self::sendRequest($payload, TRUE);
174 + if (isset($add_site['errors'])) {
158 175 WebTotemOption::setNotification('error', __('Failed to add the site to the WebTotem platform.', 'wtotem'));
159 176 } else {
160 - if ($response['data']['added']) {
177 + if ($host = $add_site['data']['auth']['sites']['create']) {
161 178 // If it added, save site ID.
162 - $host = self::getHostID($domain);
163 - WebTotemOption::setHost($domain, $host['id']);
179 + WebTotemOption::setHost($host['title'], $host['id']);
164 180 return [
165 181 'id' => $host['id'],
166 - 'name' => $host['hostname'],
182 + 'name' => $host['title'],
167 183 ];
168 184 }
169 185 }
170 186 return [];
@@ -172,9 +188,9 @@
172 188
173 189 /**
174 190 * Get all sites from API.
175 191 *
176 - * @param string $page_num
192 + * @param string $cursor
177 193 * Mark for loading data.
178 194 * @param string $limit
179 195 * Limit of sites to loading.
180 196 *
@@ -180,14 +196,20 @@
180 196 *
181 197 * @return array
182 198 * Returns host data.
183 199 */
184 - public static function getSites($page_num = 1, $page_size = 15, $status = 'active')
200 + public static function getSites($cursor = null, $limit = 15, $filter = false)
185 201 {
186 - $result = self::sendRequest('hosts', ['page_num' => $page_num, 'page_size' => $page_size, 'status' => $status], 'GET', TRUE);
202 + $cursor = ($cursor == null) ? 'null' : '\"' . $cursor . '\"';
203 + if (!$filter) {
204 + $filter = ($limit === 0) ? '' : 'pagination:{ first: ' . $limit . ', cursor: ' . $cursor . ' }';
205 + }
187 206
188 - if (isset($result['Data'])) {
189 - return $result['Data'];
207 + $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 } } } } } }"}';
208 + $result = self::sendRequest($payload, true);
209 +
210 + if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
211 + return $result['data']['auth']['viewer']['sites']['list'];
190 212 }
191 213
192 214 return [];
193 215 }
@@ -200,18 +222,18 @@
200 222 *
201 223 * @return array
202 224 * Returns host data.
203 225 */
204 - public static function getHostID($site)
226 + public static function checkForMatches($site)
205 227 {
206 - $result = self::sendRequest('hosts/id', ['hostname' => $site], 'GET', TRUE);
228 + $payload = '{"query": "query getSites { auth { viewer { sites { list(filter: { search: \"'. $site .'\" }) { edges{ node{ hostname id } } } } } } }" }';
229 + $result = self::sendRequest($payload, true);
207 230
208 - if (isset($result['data'])) {
209 - WebTotemOption::setOptions(['config_id' => $result['data']['config_id']]);
210 - return ['id' => $result['data']['host_id'], 'hostname' => $site];
231 + if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
232 + return $result['data']['auth']['viewer']['sites']['list'];
211 233 }
212 234
213 - return ['id' => '', 'hostname' => ''];
235 + return [];
214 236 }
215 237
216 238
217 239 /**
@@ -225,28 +247,28 @@
225 247 */
226 248 public static function getAgentsFiles($host_id)
227 249 {
228 250
229 -// if (WebTotem::isMultiSite()) {
230 -// $all_hosts = WebTotemOption::getOption('all_hosts');
231 -// $all_hosts = $all_hosts ? json_decode($all_hosts, true) : [];
232 -//
233 -// $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
234 -// $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
235 -//
236 -// $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
237 -// $response = self::sendRequest($payload, TRUE);
238 -//
239 -// if (isset($response['data']['auth']['am']['installMultisite'])) {
240 -// return $response['data']['auth']['am']['installMultisite'];
241 -// }
242 -// } else {
243 - $response = self::sendRequest('/agents/' . $host_id . '/install', [], 'POST', TRUE);
251 + if (WebTotem::isMultiSite()) {
252 + $all_hosts = WebTotemOption::getOption('all_hosts');
253 + $all_hosts = $all_hosts ? json_decode($all_hosts, true) : [];
244 254
245 - if (isset($response['data'])) {
246 - return $response['data'];
255 + $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
256 + $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
257 +
258 + $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
259 + $response = self::sendRequest($payload, TRUE);
260 +
261 + if (isset($response['data']['auth']['am']['installMultisite'])) {
262 + return $response['data']['auth']['am']['installMultisite'];
247 263 }
248 -// }
264 + } else {
265 + $payload = '{"query":"mutation { auth { am { install(siteId: \"' . $host_id . '\"){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
266 + $response = self::sendRequest($payload, TRUE);
267 + if (isset($response['data']['auth']['am']['install'])) {
268 + return $response['data']['auth']['am']['install'];
269 + }
270 + }
249 271 return [];
250 272 }
251 273
252 274 /**
@@ -258,30 +280,23 @@
258 280 * @return void.
259 281 */
260 282 public static function addMultiSiteNewSites($new_sites)
261 283 {
284 + // Host id of the main site in MultiSite network.
285 + $main_host = WebTotemOption::getMainHost();
262 286
263 - }
287 + foreach ($new_sites as $site) {
288 + $all_sites = self::getSites(null, 1000000);
289 + $host = self::addSite($site, $all_sites);
290 + if (key_exists('id', $host)) {
291 + $payload = '{"query":"mutation { auth { am { addMultisiteHost(mainSiteId: \"' . $main_host['id'] . '\", siteId: \"' . $host['id'] . '\") } } }"}';
264 292
265 - /**
266 - * Get the date of creation of the site.
267 - *
268 - * @param string $site
269 - * The domain we want to check.
270 - *
271 - * @return string|bool
272 - * Returns host data.
273 - */
274 - public static function getGetSiteAddedDate($site)
275 - {
276 - $payload = '{"query": "query getSites { auth { viewer { sites { list(filter: { search: \"'. $site .'\" }) { edges{ node{ createdAt } } } } } } }" }';
277 - $result = self::sendRequest($payload, true);
278 -
279 - if (isset($result['data']['auth']['viewer']['sites']['list']['edges'][0]['node']['createdAt'])) {
280 - return $result['data']['auth']['viewer']['sites']['list']['edges'][0]['node']['createdAt'];
293 + $result = self::sendRequest($payload, TRUE);
294 + if (!$result['errors'][0]['message']) {
295 + WebTotemOption::setNotification('info', __('A new website has been added: ', 'wtotem') . $site);
296 + }
297 + }
281 298 }
282 -
283 - return false;
284 299 }
285 300
286 301 /**
287 302 * Remove secondary MultiSite host.
@@ -293,8 +308,13 @@
293 308 * Returns result removing host.
294 309 */
295 310 public static function removeMultiSiteHost($host_id)
296 311 {
312 + $payload = '{"query":"mutation { auth { am { removeMultisiteHost(siteId: \"' . $host_id . '\") } } }"}';
313 + $response = self::sendRequest($payload, TRUE);
314 + if (isset($response['data']['auth']['am']['removeSecondaryMultisiteHost'])) {
315 + return $response['data']['auth']['am']['removeSecondaryMultisiteHost'];
316 + }
297 317
298 318 return false;
299 319 }
300 320
@@ -300,18 +320,21 @@
300 320
301 321 /**
302 322 * Method to get agents (AM, WAF, AV) statuses.
303 323 *
324 + * @param string $host_id
325 + * Host id on WebTotem.
326 + *
304 327 * @return array
305 328 * Returns agents statuses data.
306 329 */
307 - public static function getAgentsStatusesFromAPI()
330 + public static function getAgentsStatusesFromAPI($host_id)
308 331 {
309 - $config_id = WebTotemOption::getOption('config_id');
310 - $response = self::sendRequest('/agents/' . $config_id . '/status', [], 'GET', TRUE);
332 + $payload = '{"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { agentManager { statuses { am { status } av { status } waf { status } } } } } } } }", "variables":{"id":"' . $host_id . '"}}';
333 + $response = self::sendRequest($payload, TRUE);
311 334
312 - if (isset($response['data'])) {
313 - return $response['data'];
335 + if (isset($response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'])) {
336 + return $response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'];
314 337 }
315 338
316 339 return [];
317 340 }
@@ -333,9 +356,9 @@
333 356 return FALSE;
334 357 }
335 358
336 359 /**
337 - * Method for get monitoring data.
360 + * Method for get all the site security data.
338 361 *
339 362 * @param string $host_id
340 363 * Host id on WebTotem.
341 364 * @param int|array $days
@@ -343,14 +366,18 @@
343 366 *
344 367 * @return array
345 368 * Returns all data.
346 369 */
347 - public static function getMonitoringData($host_id, $days = 7)
370 + public static function getAllData($host_id, $days = 7)
348 371 {
349 - $response = self::sendRequest('/dashboard/monitoring/' . $host_id . '/results', [], 'GET', TRUE);
372 + $language = WebTotem::getLanguage();
373 + $period = WebTotem::getPeriod($days);
350 374
351 - if (isset($response['data'])) {
352 - return $response['data'];
375 + $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 . '"}}';
376 + $response = self::sendRequest($payload, TRUE);
377 +
378 + if (isset($response['data']['auth']['viewer']['sites']['one'])) {
379 + return $response['data']['auth']['viewer']['sites']['one'];
353 380 }
354 381
355 382 return [];
356 383 }
@@ -380,12 +407,14 @@
380 407
381 408 /**
382 409 * Method to get firewall data.
383 410 *
411 + * @param string $host_id
412 + * Host id on WebTotem.
384 413 * @param int $limit
385 414 * Limit on the number of records.
386 - * @param string $page
387 - * Page for loading data.
415 + * @param string $cursor
416 + * Mark for loading data.
388 417 * @param int|array $days
389 418 * For what period data is needed.
390 419 *
391 420 * @return array
@@ -390,23 +419,18 @@
390 419 *
391 420 * @return array
392 421 * Returns firewall data.
393 422 */
394 - public static function getFirewall($limit = 20, $page = 1, $days = 365)
423 + public static function getFirewall($host_id, $limit = 20, $cursor = NULL, $days = 365)
395 424 {
396 425 $period = WebTotem::getPeriod($days);
426 + $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
397 427
398 - $config_id = WebTotemOption::getOption('config_id');
399 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/logs', [
400 - 'page_num' => $page,
401 - 'page_size' => $limit,
402 - 'from' => $period['from'],
403 - 'to' => $period['to']
404 - ], 'GET', TRUE);
428 + $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 . '}}} }';
429 + $response = self::sendRequest($payload, TRUE);
405 430
406 -
407 - if (isset($response['data'])) {
408 - return $response['data'];
431 + if (isset($response['data']['auth']['viewer']['sites']['one'])) {
432 + return $response['data']['auth']['viewer']['sites']['one'];
409 433 }
410 434
411 435 return [];
412 436 }
@@ -413,8 +437,10 @@
413 437
414 438 /**
415 439 * Method to get firewall chart data.
416 440 *
441 + * @param string $host_id
442 + * Host id on WebTotem.
417 443 * @param int $days
418 444 * For what period data is needed.
419 445 *
420 446 * @return array
@@ -419,21 +445,17 @@
419 445 *
420 446 * @return array
421 447 * Returns firewall chart data.
422 448 */
423 - public static function getFirewallStatistics($days = 7)
449 + public static function getFirewallChart($host_id, $days = 7)
424 450 {
425 451 $period = WebTotem::getPeriod($days);
426 452
427 - $config_id = WebTotemOption::getOption('config_id');
428 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/statistics', [
429 - 'from' => $period['from'],
430 - 'to' => $period['to']
431 - ], 'GET', TRUE);
453 + $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'] . '} } }';
454 + $response = self::sendRequest($payload, TRUE);
432 455
433 -
434 - if (isset($response['data'])) {
435 - return $response['data'];
456 + if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
457 + return $response['data']['auth']['viewer']['sites']['one']['firewall'];
436 458 }
437 459
438 460 return [];
439 461 }
@@ -438,27 +460,12 @@
438 460 return [];
439 461 }
440 462
441 463 /**
442 - * Method to get firewall settings.
443 - *
444 - * @return array
445 - * Returns information whether the request was successful.
446 - */
447 - public static function getFirewallSettings()
448 - {
449 - $config_id = WebTotemOption::getOption('config_id');
450 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/configs', [], 'GET', TRUE);
451 - if (isset($response['data'])) {
452 - return $response['data'];
453 - }
454 -
455 - return [];
456 - }
457 -
458 - /**
459 464 * Method to set firewall settings.
460 465 *
466 + * @param string $host_id
467 + * Host id on WebTotem.
461 468 * @param array $settings
462 469 * User-specified settings.
463 470 *
464 471 * @return array
@@ -463,144 +470,112 @@
463 470 *
464 471 * @return array
465 472 * Returns information whether the request was successful.
466 473 */
467 - public static function setFirewallSettings(array $settings)
474 + public static function setFirewallSettings($host_id, array $settings)
468 475 {
469 - $config_id = WebTotemOption::getOption('config_id');
470 - return self::sendRequest('/dashboard/firewall/' . $config_id . '/configs', $settings, 'PATCH', TRUE);
476 + $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 } } } } }"}';
477 + return self::sendRequest($payload, TRUE);
471 478 }
472 479
473 -
474 480 /**
475 - * Method to get antivirus history data.
481 + * Method to get antivirus data.
476 482 *
477 - * @param int $page_num
478 - * Page number.
479 - * @param int $page_size
480 - * Number of entries per page.
483 + * @param array $params
484 + * Parameters for filtering data.
481 485 *
482 486 * @return array
483 - * Returns antivirus history data.
487 + * Returns antivirus data.
484 488 */
485 - public static function getAntivirusHistory($page_num = 1, $page_size = 10)
489 + public static function getAntivirus(array $params)
486 490 {
487 - $config_id = WebTotemOption::getOption('config_id');
488 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/history', [
489 - 'page_num' => $page_num,
490 - 'page_size' => $page_size
491 - ], 'GET', TRUE);
492 491
493 - if (isset($response['data'])) {
494 - return $response['data'];
492 + $cursor = ($params['cursor']) ? '"' . $params['cursor'] . '"' : 'null';
493 + $event = ($params['event']) ? '"' . $params['event'] . '"' : '"new"';
494 + $permissions = ($params['permissions']) ? ' "permissionsChanged":true, ' : '';
495 + $period = WebTotem::getPeriod($params['days']);
496 +
497 + $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 } } } } } } }"}';
498 + $response = self::sendRequest($payload, TRUE);
499 +
500 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
501 + return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
495 502 }
496 503 return [];
497 504 }
498 505
499 506 /**
500 - * Method to get antivirus history details data.
507 + * Method to get antivirus last test.
501 508 *
502 - * @param int $scan_id
503 - * Scan ID.
504 - * @param int $page_num
505 - * Page number.
506 - * @param int $page_size
507 - * Number of entries per page.
509 + * @param string $host_id
510 + * Host id on WebTotem.
508 511 *
509 512 * @return array
510 - * Returns antivirus history data.
513 + * Returns antivirus last test data.
511 514 */
512 - public static function getAntivirusHistoryDetails($scan_id, $page_num = 1, $page_size = 10)
515 + public static function getAntivirusLastTest($host_id)
513 516 {
514 - $config_id = WebTotemOption::getOption('config_id');
515 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/history/' . $scan_id . '/details', [
516 - 'page_num' => $page_num,
517 - 'page_size' => $page_size
518 - ], 'GET', TRUE);
519 517
518 + $payload = '{"variables":{"id":"' . $host_id . '"},"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { antivirus { status lastTest { time } } } } } } }"}';
519 + $response = self::sendRequest($payload, TRUE);
520 520
521 - if (isset($response['data'])) {
522 - return $response['data'];
521 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
522 + return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
523 523 }
524 524 return [];
525 525 }
526 526
527 527 /**
528 - * Method to get quarantine data.
528 + * Method to force check services.
529 529 *
530 - * @param int $page_num
531 - * Page number.
532 - * @param int $page_size
533 - * Number of entries per page.
530 + * @param string $host_id
531 + * Host id on WebTotem.
532 + * @param string $service
533 + * Service that needs to be checked.
534 534 *
535 535 * @return array
536 - * Returns quarantine data.
537 - */
538 - public static function getAntivirusCurrentDetails($page_num = 1, $page_size = 5)
539 - {
540 - $config_id = WebTotemOption::getOption('config_id');
541 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/current/details', [
542 - 'page_num' => $page_num,
543 - 'page_size' => $page_size
544 - ], 'GET', TRUE);
545 -
546 - if (isset($response['data'])) {
547 - return $response['data'];
548 - }
549 - return [];
550 - }
551 -
552 -
553 - /**
554 - * Method to force check Antivirus.
555 - *
556 - * @return mixed
557 536 * Returns information whether the request was successful.
558 537 */
559 - public static function forceCheckAV()
538 + public static function forceCheck($host_id, $service)
560 539 {
561 - $config_id = WebTotemOption::getOption('config_id');
562 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/check', [], 'POST', TRUE);
540 + $payload = '{"variables":{"id":"' . $host_id . '","service":"' . $service . '"},"query":"mutation ($id: ID!, $service: ForceCheckService!) { auth { sites { forceCheck(siteId: $id, service: $service) } } }"} ';
541 + return self::sendRequest($payload, TRUE);
563 542 }
564 543
565 -
566 544 /**
567 - * Method to force check services.
545 + * Method to export antivirus report.
568 546 *
569 547 * @param string $host_id
570 548 * Host id on WebTotem.
571 - * @param string $module_name
572 - * Service that needs to be checked.
549 + * @param int|array $days
550 + * For what period data is needed.
573 551 *
574 552 * @return array
575 553 * Returns information whether the request was successful.
576 554 */
577 - public static function forceCheck($host_id, $module_name)
555 + public static function avExport($host_id, $days = 30)
578 556 {
579 - return self::sendRequest('/dashboard/hosts/' . $host_id . '/check', ['module_name' => $module_name], 'POST', TRUE);
557 + $period = WebTotem::getPeriod($days);
558 + $payload = '{"variables":{ "input":{"siteId":"' . $host_id . '", "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} }},"query":"mutation ($input: AvLogExportInput!) { auth { sites { av { export(input: $input) } } } }"} ';
559 + return self::sendRequest($payload, TRUE);
580 560 }
581 561
582 562 /**
583 563 * Method to get quarantine data.
584 564 *
585 - * @param int $page_num
586 - * Page number.
587 - * @param int $page_size
588 - * Number of entries per page.
565 + * @param string $host_id
566 + * Host id on WebTotem.
589 567 *
590 568 * @return array
591 569 * Returns quarantine data.
592 570 */
593 - public static function getQuarantineList($page_num = 1, $page_size = 5)
571 + public static function getQuarantineList($host_id)
594 572 {
595 - $config_id = WebTotemOption::getOption('config_id');
596 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine', [
597 - 'page_num' => $page_num,
598 - 'page_size' => $page_size
599 - ], 'GET', TRUE);
573 + $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ antivirus{ quarantine{ id path date } } } } } } } "}';
574 + $response = self::sendRequest($payload, TRUE);
600 575
601 - if (isset($response['data'])) {
602 - return $response['data'];
576 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'])) {
577 + return $response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'];
603 578 }
604 579 return [];
605 580 }
606 581
@@ -606,8 +581,10 @@
606 581
607 582 /**
608 583 * Method to move file to quarantine.
609 584 *
585 + * @param string $host_id
586 + * Host id on WebTotem.
610 587 * @param string $path
611 588 * Path to the file.
612 589 *
613 590 * @return array
@@ -612,30 +589,27 @@
612 589 *
613 590 * @return array
614 591 * Returns information whether the request was successful.
615 592 */
616 - public static function moveToQuarantine($path)
593 + public static function moveToQuarantine($host_id, $path)
617 594 {
618 - $config_id = WebTotemOption::getOption('config_id');
619 -
620 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine/' . $path . '/to-quarantine',
621 - [], 'POST', TRUE);
595 + $payload = '{"query":"mutation{ auth{ sites{ av{ moveToQuarantine(input:{ siteId:\"' . $host_id . '\", path:\"' . $path . '\" }) } } } } "}';
596 + return self::sendRequest($payload, TRUE);
622 597 }
623 598
624 599 /**
625 600 * Method to move file from quarantine.
626 601 *
627 - * @param string $path
628 - * Path to the file.
602 + * @param string $id
603 + * Id assigned to the file.
629 604 *
630 605 * @return array
631 606 * Returns information whether the request was successful.
632 607 */
633 - public static function moveFromQuarantine($path)
608 + public static function moveFromQuarantine($id)
634 609 {
635 - $config_id = WebTotemOption::getOption('config_id');
636 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine/' . $path . '/from-quarantine',
637 - [], 'POST', TRUE);
610 + $payload = '{"query":"mutation{ auth{ sites{ av{ moveFromQuarantine(id: \"' . $id . '\") } } } } "}';
611 + return self::sendRequest($payload, TRUE);
638 612 }
639 613
640 614 /**
641 615 * Method to get server status data.
@@ -882,23 +856,21 @@
882 856
883 857 /**
884 858 * Method to get allow/deny ip list.
885 859 *
886 - * @param string $type
887 - * Type of ip list
860 + * @param string $host_id
861 + * Host id on WebTotem.
888 862 *
889 863 * @return array|bool
890 864 * Returns ip allow/deny lists.
891 865 */
892 - public static function getIpLists($type = 'blacklist')
866 + public static function getIpLists($host_id)
893 867 {
894 - $config_id = WebTotemOption::getOption('config_id');
895 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist', [
896 - 'type' => $type,
897 - ], 'GET', TRUE);
868 + $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 } } } } } } }"} ';
869 + $response = self::sendRequest($payload, TRUE);
898 870
899 - if (isset($response['data'])) {
900 - return $response['data'];
871 + if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
872 + return $response['data']['auth']['viewer']['sites']['one']['firewall'];
901 873 }
902 874
903 875 return [];
904 876 }
@@ -905,45 +877,53 @@
905 877
906 878 /**
907 879 * Method to add ip to allow/deny list.
908 880 *
909 - * @param string $ip
910 - * Ip address.
911 - * @param string $type
912 - * Allow or deny type.
881 + * @param string $host_id
882 + * Host id on WebTotem.
883 + * @param string $ips
884 + * Ip address list.
885 + * @param string $list
886 + * Allow or deny list.
913 887 *
914 888 * @return bool
915 889 * Returns information whether the request was successful.
916 890 */
917 - public static function addIpToList($ips, $type)
891 + public static function addIpToList($host_id, $ips, $list)
918 892 {
919 - $config_id = WebTotemOption::getOption('config_id');
920 - self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist?type=' . $type, [
921 - 'ip' => array_filter($ips),
922 - ], 'POST', TRUE);
923 893
924 - return true;
894 + if ($ips) {
895 + $ips = WebTotem::convertIpListForApi($ips);
896 + $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "ips": ' . $ips . ', "color": "' . $list . '" } }, "query":"mutation($input: WafListInput!) { auth { sites { waf { addToList(input: $input){ status invalidIPs} } } } }"} ';
897 + $response = self::sendRequest($payload, TRUE);
898 +
899 + if (isset($response['data']['auth']['sites']['waf']['addToList'])) {
900 + return $response['data']['auth']['sites']['waf']['addToList'];
901 + }
902 + }
903 +
904 + return FALSE;
925 905 }
926 906
927 907 /**
928 908 * Method to remove ip from allow/deny list by id.
929 909 *
930 - * @param string $ip
931 - * Ip address.
932 - * @param string $type
933 - * Allow or deny type.
910 + * @param string $id
911 + * Id assignment to ip address.
934 912 *
935 913 * @return bool
936 914 * Returns information whether the request was successful.
937 915 */
938 - public static function removeIpFromList($ip, $type)
916 + public static function removeIpFromList($id)
939 917 {
940 - $config_id = WebTotemOption::getOption('config_id');
941 - self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist?type=' . $type, [
942 - 'ip' => $ip,
943 - ], 'DELETE', TRUE);
918 + $payload = '{"variables":{ "id": "' . $id . '" },"query":"mutation($id: ID!) { auth { sites { waf { removeFromList(id: $id) } } } }"} ';
919 + $response = self::sendRequest($payload, TRUE);
944 920
945 - return true;
921 + if (isset($response['data']['auth']['sites']['waf']['removeFromList'])) {
922 + return $response['data']['auth']['sites']['waf']['removeFromList'];
923 + }
924 +
925 + return FALSE;
946 926 }
947 927
948 928 /**
949 929 * Method to get allow url list.
@@ -1159,9 +1139,9 @@
1159 1139
1160 1140 $http_code = wp_remote_retrieve_response_code($response);
1161 1141
1162 1142 if ($http_code < 200) {
1163 - // WebTotemOption::setNotification('error', __('Could not connect to feedback endpoint.', 'wtotem'));
1143 + WebTotemOption::setNotification('error', __('Could not connect to feedback endpoint.', 'wtotem'));
1164 1144 return [];
1165 1145 }
1166 1146
1167 1147 $response_body = wp_remote_retrieve_body($response);
@@ -1168,120 +1148,112 @@
1168 1148 return json_decode($response_body, true);
1169 1149 }
1170 1150
1171 1151 /**
1172 - * Sends a REST API request to the WebTotem API server.
1152 + * Function sends GraphQL request to API server.
1173 1153 *
1174 - * @param string $endpoint
1175 - * REST API endpoint (e.g., 'scan', 'status', etc.).
1176 - * @param array $data
1177 - * Associative array of data to send as JSON body or query parameters.
1178 - * @param string $method
1179 - * HTTP method: GET, POST, PUT, DELETE (default is POST).
1180 - * @param bool $useToken
1181 - * Whether to include the auth token.
1182 - * @param bool $retry
1183 - * Used to prevent recursion on token renewal.
1154 + * @param string $payload
1155 + * Payload to be sent to API server.
1156 + * @param bool $token
1157 + * Whether a token is needed when sending a request.
1158 + * @param bool $repeat
1159 + * Required to avoid recursion.
1184 1160 *
1185 - * @return array|null
1186 - * API response as an associative array, or null on failure.
1161 + * @return array
1162 + * Returns response from WebTotem API.
1187 1163 */
1188 - protected static function sendRequest($endpoint, $data = [], $method = 'POST', $useToken = false, $retry = false)
1164 + protected static function sendRequest($payload, $token = FALSE, $repeat = FALSE)
1189 1165 {
1190 1166 $api_key = WebTotemOption::getOption('api_key');
1191 1167
1192 - // Get or initialize the API URL.
1168 + // Remote URL where the public WebTotem API service is running.
1193 1169 $api_url = WebTotemOption::getOption('api_url');
1194 1170 if (!$api_url) {
1195 - $api_url = self::getApiUrl();
1171 + $api_url = self::getApiUrl('P');
1196 1172 WebTotemOption::setOptions(['api_url' => $api_url]);
1197 1173 }
1198 1174
1199 - $auth_token = null;
1200 - if ($useToken) {
1175 + // Checking whether a token is needed.
1176 + if ($token) {
1201 1177 $auth_token = WebTotemOption::getOption('auth_token');
1202 1178 $auth_token_expired = WebTotemOption::getOption('auth_token_expired');
1203 1179
1204 - if ($auth_token_expired <= time() && !$retry) {
1180 + // Checking whether the token has expired.
1181 + if ($auth_token_expired <= time() && !$repeat) {
1205 1182 $result = self::auth($api_key);
1206 1183 if ($result === 'success') {
1207 - return self::sendRequest($endpoint, $data, $method, $useToken, true);
1208 - } elseif (isset($result['message'])) {
1209 - $message = WebTotem::messageForHuman($result['message']);
1210 -// WebTotemOption::setNotification('info', '$endpoint: ' . $endpoint);
1211 - WebTotemOption::setNotification('error', $message);
1184 + return self::sendRequest($payload, $token, TRUE);
1185 + } else {
1186 + if (isset($result['errors'])) {
1187 + $message = WebTotem::messageForHuman($result['errors'][0]['message']);
1188 + WebTotemOption::setNotification('error', $message);
1189 + }
1212 1190 }
1213 1191 }
1214 1192 }
1215 1193
1216 - $url = rtrim($api_url, '/') . '/api/v1/' . ltrim($endpoint, '/');
1194 + if (function_exists('wp_remote_post')) {
1217 1195
1218 - $args = [
1219 - 'timeout' => 60,
1220 - 'sslverify' => false,
1221 - 'headers' => [
1222 - 'Accept' => 'application/json',
1223 - 'Content-Type' => 'application/json',
1224 - 'source' => 'WORDPRESS',
1225 - ],
1226 - ];
1196 + $args = [
1197 + 'body' => $payload,
1198 + 'timeout' => '60',
1199 + 'sslverify' => false,
1200 + 'headers' => [
1201 + 'Content-Type:application/json',
1202 + 'Content-Type' => 'application/json',
1203 + 'Accept: application/json',
1204 + 'source: WORDPRESS',
1205 + ],
1206 + ];
1227 1207
1228 - if ($auth_token) {
1229 - $args['headers']['Authorization'] = "Bearer $auth_token";
1230 - }
1208 + if (isset($auth_token)) {
1209 + $auth = "Bearer " . $auth_token;
1210 + $args['headers'] = array_merge($args['headers'], ["Authorization" => $auth]);
1211 + }
1231 1212
1232 - if (strtoupper($method) === 'GET') {
1233 - $url = add_query_arg($data, $url);
1213 + $response = wp_remote_post($api_url, $args);
1214 + $response = wp_remote_retrieve_body($response);
1215 + $response = json_decode($response, true);
1216 +
1234 1217 } else {
1235 - $args['body'] = json_encode($data);
1218 + $error = 'WP_REMOTE_POST_NOT_EXIST';
1236 1219 }
1237 1220
1238 - $response = wp_remote_request($url, array_merge($args, ['method' => strtoupper($method)]));
1239 -
1240 - $errors = ['status' => false];
1241 - if (is_wp_error($response)) {
1242 - $errors = [
1243 - 'status' => true,
1244 - 'message' => 'SERVER UNAVAILABLE: ' . $response->get_error_message(),
1245 - ];
1246 - }
1247 -
1248 - $body = wp_remote_retrieve_body($response);
1249 - $decoded = json_decode($body, true);
1250 -
1251 - if (isset($decoded['message']) or $errors['status']) {
1252 - $errorMessage = $errors['status'] ? $errors['message'] : $decoded['message'];
1253 -
1254 - if (stripos($errorMessage, "Password expired") !== false) {
1221 + // Checking if there are errors in the response.
1222 + if (isset($response['errors'][0]['message'])) {
1223 + // Show error page if WebTotem cabinet's password is expired.
1224 + if (stripos($response['errors'][0]['message'], "Password expired") !== FALSE) {
1255 1225 wtotem_error_page(['errors' => 'PASSWORD_EXPIRED']);
1256 1226 exit();
1257 - } elseif (stripos($errorMessage, "API_KEY_DEACTIVATED") !== false) {
1227 + }
1228 +
1229 + if (stripos($response['errors'][0]['message'], "API_KEY_DEACTIVATED") !== FALSE) {
1258 1230 wtotem_error_page(['errors' => 'TARIFF_EXPIRED']);
1259 1231 exit();
1260 1232 }
1261 1233
1262 - $message = WebTotem::messageForHuman($errorMessage);
1263 - if ($errorMessage == "invalid credentials" && !$retry) {
1264 -
1265 - if (self::auth($api_key) === 'success') {
1266 -
1267 - return self::sendRequest($endpoint, $data, $method, $useToken, true);
1234 + $message = WebTotem::messageForHuman($response['errors'][0]['message']);
1235 + if (stripos($response['errors'][0]['message'], "INVALID_TOKEN") !== FALSE && !$repeat) {
1236 + $response = self::auth($api_key);
1237 + if ($response === 'success') {
1238 + return self::sendRequest($payload, $token, TRUE);
1268 1239 }
1269 - } elseif (stripos($errorMessage, "USERHOST_NOT_BELONG_TO_USER") !== false) {
1240 + } elseif (stripos($response['errors'][0]['message'], "USERHOST_NOT_BELONG_TO_USER") !== FALSE) {
1270 1241 if (WebTotem::isMultiSite()) {
1271 1242 WebTotemOption::clearAllHosts();
1243 + WebTotemOption::clearOptions(['host_id', 'host_name']);
1244 + } else {
1245 + WebTotemOption::clearOptions(['host_id', 'host_name']);
1272 1246 }
1273 - WebTotemOption::clearOptions(['host_id', 'host_name']);
1274 1247 } else {
1275 -// WebTotemOption::setNotification('info', '$endpoint: ' . $endpoint);
1276 1248 WebTotemOption::setNotification('error', $message);
1277 1249 }
1278 1250 }
1279 1251
1280 -// if (empty($decoded)) {
1281 -// self::checkEndpoint();
1282 -// }
1252 + if (!empty($error)) {
1253 + WebTotemOption::setNotification('error', $error);
1254 + }
1283 1255
1284 - return $decoded;
1256 + return $response;
1285 1257 }
1286 1258
1287 1259 }