PluginProbe
WebTotem Security / 2.4.14
WebTotem Security v2.4.14
3.0.2 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 All 110 releases
← All changes | lib/API.php +826 -854 3.0.22.4.14 View file →
@@ -1,11 +1,11 @@
1 1 <?php
2 2
3 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!");
4 + if (!headers_sent()) {
5 + header('HTTP/1.1 403 Forbidden');
6 + }
7 + die("Protected By WebTotem!");
8 8 }
9 9
10 10 /**
11 11 * WebTotem API class.
@@ -15,1001 +15,973 @@
15 15 * @version 1.0
16 16 * @copyright (C) 2022 WebTotem team (http://wtotem.com)
17 17 * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
18 18 */
19 -class WebTotemAPI extends WebTotem
20 -{
19 +class WebTotemAPI extends WebTotem {
21 20
22 - /**
23 - * HTTP status of the most recent API call, 0 when the request never landed.
24 - *
25 - * @var int
26 - */
27 - protected static $last_status = 0;
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;
28 32
29 - /**
30 - * HTTP status of the most recent API call.
31 - *
32 - * @return int
33 - * Status code, or 0 when the request did not reach the server.
34 - */
35 - public static function getLastStatus()
36 - {
37 - return (int) self::$last_status;
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);
38 42 }
39 43
40 - /**
41 - * Raises a notification unless the caller asked to stay quiet.
42 - *
43 - * Probing calls (such as the WebSocket ticket, which is expected to be
44 - * missing on older API builds) must not spam the admin with errors.
45 - *
46 - * @param bool $silent
47 - * TRUE to swallow the notification.
48 - * @param string $type
49 - * Notification type.
50 - * @param string $message
51 - * Notification text.
52 - *
53 - * @return void
54 - */
55 - protected static function notify($silent, $type, $message)
56 - {
57 - if (!$silent) {
58 - WebTotemOption::setNotification($type, $message);
59 - }
44 + $payload = '{"query":"mutation{ guest{ apiKeys{ auth(apiKey:\"' . $api_key . '\", source:\"' . $domain . '\"),{ token{ value, refreshToken, expiresIn } } } } }"}';
45 + $result = self::sendRequest($payload, FALSE, TRUE);
46 +
47 + if (isset($result['data']['guest']['apiKeys']['auth']['token']['value'])) {
48 + $auth_token = $result['data']['guest']['apiKeys']['auth']['token'];
49 + WebTotemOption::login(['token' => $auth_token, 'api_key' => $api_key]);
50 + return 'success';
51 + } elseif($result['errors'][0]['message'] == 'INVALID_API_KEY') {
52 + WebTotemOption::logout();
60 53 }
61 54
55 + return FALSE;
56 + }
62 57
63 - /**
64 - * Method for getting an auth token.
65 - *
66 - * @param string $api_key
67 - * Application programming interface key.
68 - *
69 - * @return bool|string
70 - * Returns auth status
71 - */
72 - public static function auth($api_key, $repeat = FALSE)
73 - {
74 - $domain = WEBTOTEM_SITE_DOMAIN;
58 + /**
59 + * Method for getting API url.
60 + *
61 + * @param string $prefix
62 + *
63 + * @return string|bool
64 + * API url
65 + */
66 + public static function getApiUrl($prefix){
67 + $urls = [
68 + 'P' => '.wtotem.com',
69 + 'C' => '.webtotem.kz',
70 + ];
75 71
76 - if (empty($api_key)) {
77 - return FALSE;
78 - }
72 + if(array_key_exists($prefix, $urls)){
73 + return 'https://api' . $urls[$prefix] . '/graphql';
74 + }
75 + return false;
76 + }
79 77
80 - $data = ['api_key' => $api_key, 'site' => $domain];
81 - $result = self::sendRequest('auth/sign-in/api-key', $data, 'POST', FALSE, TRUE);
78 + /**
79 + * Get site info from API server.
80 + *
81 + * @param string $attempt
82 + * Is the request an attempt to get host data.
83 + *
84 + * @return array
85 + * Returns host data.
86 + */
87 + public static function siteInfo($attempt = FALSE) {
82 88
83 - if($result === null){
84 - WebTotemOption::setNotification('warning' , __('Authorization failed. The server may be temporarily unavailable', 'wtotem'));
85 - }
89 + if(self::isMultiSite()){
90 + $host['id'] = WebTotemOption::getSessionOption('host_id');
86 91
87 - if (isset($result['access_token'])) {
88 - $auth_token = $result['access_token'];
89 - if(!WebTotemOption::isActivated()){
90 - WebTotemOption::login(['token' => $auth_token, 'api_key' => $api_key]);
91 - WebTotemAgentManager::postdelete();
92 - } else {
93 - WebTotemOption::refreshToken($auth_token);
94 - }
92 + if ($host['id']) {
93 + return $host;
94 + }
95 + }
95 96
96 - return 'success';
97 - } elseif (isset($result['message']) and $result['message'] == 'invalid credentials') {
98 - WebTotemOption::logout();
99 - }
97 + $host = WebTotemOption::getHost();
100 98
101 - if($repeat == false){
102 - //self::checkEndpoint();
103 - return self::auth($api_key, true);
104 - }
105 -
106 - return FALSE;
99 + if ($host['id']) {
100 + return $host;
107 101 }
108 102
109 - /**
110 - * Method for getting API url.
111 - *
112 - * @return string|bool
113 - * API url
114 - */
115 - public static function getApiUrl()
116 - {
117 - return 'https://app.wtotem.com';
118 - }
103 + $all_sites = self::getSites(null, 0);
104 + if($all_sites){
105 + if(self::isMultiSite()) {
106 + $sites = get_sites();
107 + foreach ($sites as $site){
108 + $domain = untrailingslashit($site->domain . $site->path);
109 + self::addSite($domain, $all_sites);
110 + }
119 111
120 - /**
121 - * Method for getting the WebSocket endpoint url.
122 - *
123 - * @return string
124 - * WebSocket url, without any credentials.
125 - */
126 - public static function getWsUrl()
127 - {
128 - $api_url = WebTotemOption::getOption('api_url');
129 - if (!$api_url) {
130 - $api_url = self::getApiUrl();
131 - }
112 + if (!$attempt) {
113 + return self::siteInfo(TRUE);
114 + }
115 + }
116 + else {
117 + $domain = WEBTOTEM_SITE_DOMAIN;
118 + return self::addSite($domain, $all_sites);
119 + }
120 + }
132 121
133 - return preg_replace('#^http#i', 'ws', rtrim($api_url, '/')) . '/api/v1/ws';
134 - }
122 + return [];
123 + }
135 124
125 + /**
126 + * Method for adding a site to the WebTotem platform.
127 + *
128 + * @param string $domain
129 + * Domain to add.
130 + * @param array $all_sites
131 + * Array with site data on the WebTotem platform.
132 + *
133 + * @return array
134 + * Returns host data.
135 + */
136 + public static function addSite( $domain, $all_sites) {
136 137
137 - /**
138 - * Get site info from API server.
139 - *
140 - * @param string $attempt
141 - * Is the request an attempt to get host data.
142 - *
143 - * @return array
144 - * Returns host data.
145 - */
146 - public static function siteInfo($attempt = FALSE)
147 - {
148 - if (self::isMultiSite()) {
149 - $host['id'] = WebTotemOption::getSessionOption('host_id');
150 - $host['name'] = WebTotemOption::getSessionOption('host_name');
138 + if(function_exists('idn_to_utf8')){
139 + $domain = idn_to_utf8($domain);
140 + }
151 141
152 - if ($host['id']) {
153 - return $host;
154 - }
155 - }
142 + // Checking if the site has been added to the WebTotem.
143 + if(array_key_exists('edges', $all_sites)){
156 144
157 - $host = WebTotemOption::getHost();
145 + foreach ($all_sites['edges'] as $site){
146 + $site = $site['node'];
147 + $hostname = untrailingslashit($site['hostname']);
148 + // If it added, save site data to DB.
149 + if($hostname == $domain or $hostname == 'www.' . $domain){
150 + WebTotemOption::setHost($site['hostname'], $site['id']);
151 + return [
152 + 'id' => $site['id'],
153 + 'name' => $site['hostname'],
154 + ];
155 + }
156 + }
158 157
159 - if ($host['id']) {
160 - return $host;
161 - }
158 + }
162 159
163 -// if (self::isMultiSite()) {
164 -// $sites = get_sites();
165 -// foreach ($sites as $site) {
166 -// $domain = untrailingslashit($site->domain . $site->path);
167 -// self::addSite($domain);
168 -// }
169 -//
170 -// if (!$attempt) {
171 -// return self::siteInfo(TRUE);
172 -// }
173 -// } else {
174 -// $domain = WEBTOTEM_SITE_DOMAIN;
175 -// return self::addSite($domain);
176 -// }
177 - $domain = WEBTOTEM_SITE_DOMAIN;
178 - return self::addSite($domain);
160 + // If the site is not added then try to add.
161 + $payload = '{"variables":{"input":{"title":"' . $domain . '","hostname":"' . $domain . '","configs":{"scheme":"http","port":80,"wa":{},"dec":{},"ps":{}}}},"query":"mutation ($input: CreateSiteInput) { auth { sites { create(input: $input) { id hostname title } } } }"}';
162 + $add_site = self::sendRequest($payload, TRUE);
163 + if (isset($add_site['errors'])) {
164 + WebTotemOption::setNotification('error', __('Failed to add the site to the WebTotem platform.', 'wtotem'));
165 + }
166 + else {
167 + if($host = $add_site['data']['auth']['sites']['create']) {
168 + // If it added, save site ID.
169 + WebTotemOption::setHost($host['title'], $host['id']);
170 + return [
171 + 'id' => $host['id'],
172 + 'name' => $host['title'],
173 + ];
174 + }
175 + }
176 + return [];
177 + }
179 178
180 -// return [];
181 - }
179 + /**
180 + * Get all sites from API.
181 + *
182 + * @param string $cursor
183 + * Mark for loading data.
184 + * @param string $limit
185 + * Limit of sites to loading.
186 + *
187 + * @return array
188 + * Returns host data.
189 + */
190 + public static function getSites($cursor = null, $limit = 15, $filter = false) {
191 + $cursor = ($cursor == null) ? 'null' : '\"' . $cursor . '\"';
192 + if(!$filter) {
193 + $filter = ($limit === 0) ? '' : 'pagination:{ first: ' . $limit . ', cursor: ' . $cursor . ' }';
194 + }
182 195
183 - /**
184 - * Method for adding a site to the WebTotem platform.
185 - *
186 - * @param string $domain
187 - * Domain to add.
188 - *
189 - * @return array
190 - * Returns host data.
191 - */
192 - public static function addSite($domain)
193 - {
194 - if (function_exists('idn_to_utf8')) {
195 - $domain = idn_to_utf8($domain);
196 - }
196 + $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 } domain { status } antivirus { status } firewall { status } maliciousScript { stack { name } } } } } }"}';
197 + $result = self::sendRequest($payload, true);
197 198
198 - // Checking if the site has been added to the WebTotem.
199 - if(!$host = self::getHostID($domain)){
200 - $host = self::getHostID('www.' . $domain);
201 - }
199 + if (isset($result['data']['auth']['viewer']['sites']['list']['edges'])) {
200 + return $result['data']['auth']['viewer']['sites']['list'];
201 + }
202 202
203 - if($host['id']){
204 - // Remember the binding: otherwise every page load asks the API
205 - // for the host id again (and again for the www. variant).
206 - WebTotemOption::setHost($host['hostname'], $host['id']);
203 + return [];
204 + }
207 205
208 - return [
209 - 'id' => $host['id'],
210 - 'name' => $host['hostname'],
211 - ];
212 - }
206 + /**
207 + * Method to get the agents file names and AM file link.
208 + *
209 + * @param string $host_id
210 + * Host id on WebTotem.
211 + *
212 + * @return array
213 + * Returns agents files data.
214 + */
215 + public static function getAgentsFiles($host_id) {
213 216
214 - // If the site is not added then try to add.
215 - $data = ['hosts' => [$domain]];
216 - $response = self::sendRequest('hosts', $data, 'POST', TRUE);
217 + if(WebTotem::isMultiSite()){
218 + $all_hosts = WebTotemOption::getOption('all_hosts') ?: [];
219 + $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
220 + $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
217 221
218 - if (isset($response['message'])) {
219 - WebTotemOption::setNotification('error', __('Failed to add the site to the WebTotem platform.', 'wtotem'));
220 - } else {
221 - if ($response['data']['added']) {
222 - // If it added, save site ID.
223 - $host = self::getHostID($domain);
224 - WebTotemOption::setHost($domain, $host['id']);
225 - return [
226 - 'id' => $host['id'],
227 - 'name' => $host['hostname'],
228 - ];
229 - }
230 - }
231 - return [];
232 - }
222 + $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
223 + $response = self::sendRequest($payload, TRUE);
233 224
234 - /**
235 - * Get all sites from API.
236 - *
237 - * @param string $page_num
238 - * Mark for loading data.
239 - * @param string $limit
240 - * Limit of sites to loading.
241 - *
242 - * @return array
243 - * Returns host data.
244 - */
245 - public static function getSites($page_num = 1, $page_size = 15, $status = 'active', $hostname = '')
246 - {
247 - $query = [
248 - 'page_num' => $page_num,
249 - 'page_size' => $page_size,
250 - 'status' => $status,
251 - ];
225 + return $response['data']['auth']['am']['installMultisite'];
226 + }
227 + else {
228 + $payload = '{"query":"mutation { auth { am { install(siteId: \"' . $host_id . '\"){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
229 + $response = self::sendRequest($payload, TRUE);
230 + return $response['data']['auth']['am']['install'];
231 + }
232 + }
252 233
253 - if ($hostname !== '') {
254 - $query['hostname'] = $hostname;
255 - }
234 + /**
235 + * Add secondary MultiSite host.
236 + *
237 + * @param $new_sites
238 + * An array with sites to add.
239 + *
240 + * @return void.
241 + */
242 + public static function addMultiSiteNewSites($new_sites){
243 + // Host id of the main site in MultiSite network.
244 + $main_host = WebTotemOption::getMainHost();
256 245
257 - $result = self::sendRequest('hosts', $query, 'GET', TRUE);
246 + foreach ($new_sites as $site){
247 + $all_sites = self::getSites(null, 0);
248 + $host = self::addSite($site, $all_sites);
249 + if(key_exists('id', $host)){
250 + $payload = '{"query":"mutation { auth { am { addMultisiteHost(mainSiteId: \"' . $main_host['id'] . '\", siteId: \"' . $host['id'] . '\") } } }"}';
258 251
259 - // The API answers { "data": { "hosts": [...], "host_limit": n, "can_defrost": bool } }.
260 - $payload = self::payload($result);
252 + $result = self::sendRequest($payload, TRUE);
253 + if(!$result['errors'][0]['message']){
254 + WebTotemOption::setNotification( 'info', __('A new website has been added: ', 'wtotem') . $site);
255 + }
256 + }
257 + }
258 + }
261 259
262 - return isset($payload['hosts']) && is_array($payload['hosts']) ? $payload['hosts'] : [];
263 - }
260 + /**
261 + * Remove secondary MultiSite host.
262 + *
263 + * @param $host_id
264 + * Host id on WebTotem.
265 + *
266 + * @return bool
267 + * Returns result removing host.
268 + */
269 + public static function removeMultiSiteHost($host_id){
270 + $payload = '{"query":"mutation { auth { am { removeMultisiteHost(siteId: \"' . $host_id . '\") } } }"}';
271 + $response = self::sendRequest($payload, TRUE);
272 + return $response['data']['auth']['am']['removeSecondaryMultisiteHost'];
273 + }
264 274
265 - /**
266 - * Reads the payload out of the API response envelope.
267 - *
268 - * Most endpoints answer { "data": ... }; a few older builds used "Data".
269 - *
270 - * @param mixed $response
271 - * Decoded API response.
272 - *
273 - * @return array
274 - * Payload, or an empty array when the response carried none.
275 - */
276 - protected static function payload($response)
277 - {
278 - if (!is_array($response)) {
279 - return [];
280 - }
275 + /**
276 + * Method to get agents (AM, WAF, AV) statuses.
277 + *
278 + * @param string $host_id
279 + * Host id on WebTotem.
280 + *
281 + * @return array
282 + * Returns agents statuses data.
283 + */
284 + public static function getAgentsStatusesFromAPI($host_id) {
285 + $payload = '{"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { agentManager { statuses { am { status } av { status } waf { status } } } } } } } }", "variables":{"id":"' . $host_id . '"}}';
286 + $response = self::sendRequest($payload, TRUE);
281 287
282 - foreach (['data', 'Data'] as $key) {
283 - if (isset($response[$key]) && is_array($response[$key])) {
284 - return $response[$key];
285 - }
286 - }
287 -
288 - return [];
288 + if (isset($response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'])) {
289 + return $response['data']['auth']['viewer']['sites']['one']['agentManager']['statuses'];
289 290 }
290 291
291 - /**
292 - * Requests a single-use ticket for the browser WebSocket connection.
293 - *
294 - * The ticket replaces the access token that used to be printed into the
295 - * page: it is short-lived, may be used once, and is bound to the browser
296 - * Origin it was issued for.
297 - *
298 - * @param string $origin
299 - * Browser origin the ticket is issued for, e.g. https://example.com.
300 - *
301 - * @return string
302 - * The ticket, or an empty string when it could not be obtained.
303 - */
304 - public static function getWSTicket($origin = '')
305 - {
306 - $query = [];
307 - if (is_string($origin) && $origin !== '') {
308 - // add_query_arg() does not encode values; the origin carries "://".
309 - $query['origin'] = rawurlencode($origin);
310 - }
292 + return [];
293 + }
311 294
312 - $response = self::sendRequest('ws/ticket', $query, 'GET', TRUE, FALSE, TRUE);
295 + /**
296 + * Method to get user time zone.
297 + *
298 + * @return string|bool
299 + * Returns time zone data.
300 + */
301 + public static function getTimeZone() {
302 + $payload = '{"query":"query { auth { viewer{ timezone } } } "}';
303 + $response = self::sendRequest($payload, TRUE);
313 304
314 - if (is_array($response) && !empty($response['ticket'])) {
315 - return (string) $response['ticket'];
316 - }
317 -
318 - // Tolerate a wrapped answer in case the endpoint starts using the envelope.
319 - $payload = self::payload($response);
320 -
321 - return !empty($payload['ticket']) ? (string) $payload['ticket'] : '';
305 + if (isset($response['data']['auth']['viewer']['timezone'])) {
306 + return $response['data']['auth']['viewer']['timezone'];
322 307 }
308 + return FALSE;
309 + }
323 310
324 - /**
325 - * Check the site's presence in the list on the API side.
326 - *
327 - * @param string $site
328 - * The domain we want to check.
329 - *
330 - * @return array
331 - * Returns host data.
332 - */
333 - public static function getHostID($site)
334 - {
335 - $result = self::sendRequest('hosts/id', ['hostname' => $site], 'GET', TRUE);
311 + /**
312 + * Method for get all the site security data.
313 + *
314 + * @param string $host_id
315 + * Host id on WebTotem.
316 + * @param int|array $days
317 + * For what period data is needed.
318 + *
319 + * @return array
320 + * Returns all data.
321 + */
322 + public static function getAllData($host_id, $days = 7) {
323 + $language = WebTotem::getLanguage();
324 + $period = WebTotem::getPeriod($days);
336 325
337 - if (isset($result['data'])) {
338 - WebTotemOption::setOptions(['config_id' => $result['data']['config_id']]);
339 - return ['id' => $result['data']['host_id'], 'hostname' => $site];
340 - }
326 + $payload = '{"query":"query($id: ID!, $dateRange: DateRangeInput!, $language: Language!, $dateRangeWeek: DateRangeInput!, $wafLogFilter: WafLogFilter!) { auth { viewer { sites { one(id: $id) { ports { status ip tcp ignorePorts lastTest { time } } availability { status lastTest { time } responseTime downTime(dateRange: $dateRange) percent(dateRange: $dateRange) } deface { status lastTest { time } words count } domain { status registrar owner email createdDate expiredDate } ports { status lastTest { time } ip tcp country } 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 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 . '"}}';
327 + $response = self::sendRequest($payload, TRUE);
341 328
342 - return ['id' => '', 'hostname' => ''];
329 + if (isset($response['data']['auth']['viewer']['sites']['one'])) {
330 + return $response['data']['auth']['viewer']['sites']['one'];
343 331 }
344 332
333 + return [];
334 + }
345 335
346 - /**
347 - * Method to get the agents file names and AM file link.
348 - *
349 - * @param string $host_id
350 - * Host id on WebTotem.
351 - *
352 - * @return array
353 - * Returns agents files data.
354 - */
355 - public static function getAgentsFiles($host_id)
356 - {
336 + /**
337 + * Method to get firewall data.
338 + *
339 + * @param string $host_id
340 + * Host id on WebTotem.
341 + * @param int $limit
342 + * Limit on the number of records.
343 + * @param string $cursor
344 + * Mark for loading data.
345 + * @param int|array $days
346 + * For what period data is needed.
347 + *
348 + * @return array
349 + * Returns firewall data.
350 + */
351 + public static function getFirewall($host_id, $limit = 20, $cursor = NULL, $days = 365) {
352 + $period = WebTotem::getPeriod($days);
353 + $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
357 354
358 -// if (WebTotem::isMultiSite()) {
359 -// $all_hosts = WebTotemOption::getOption('all_hosts');
360 -// $all_hosts = $all_hosts ? json_decode($all_hosts, true) : [];
361 -//
362 -// $siteIdsArray = $all_hosts ? array_values($all_hosts) : [];
363 -// $siteIds = $siteIdsArray ? addslashes(WebTotem::convertArrayToString($siteIdsArray)) : '';
364 -//
365 -// $payload = '{"query":"mutation { auth { am { installMultisite(mainSiteId: \"' . $host_id . '\", siteIds: [' . $siteIds . ']){ downloadLink, amFilename, wafFilename, avFilename } } } }"}';
366 -// $response = self::sendRequest($payload, TRUE);
367 -//
368 -// if (isset($response['data']['auth']['am']['installMultisite'])) {
369 -// return $response['data']['auth']['am']['installMultisite'];
370 -// }
371 -// } else {
372 - $response = self::sendRequest('/agents/' . $host_id . '/install', [], 'POST', TRUE);
355 + $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 . '}}} }';
356 + $response = self::sendRequest($payload, TRUE);
373 357
374 - if (isset($response['data'])) {
375 - return $response['data'];
376 - }
377 -// }
378 - return [];
379 - }
358 + if (isset($response['data']['auth']['viewer']['sites']['one'])) {
359 + return $response['data']['auth']['viewer']['sites']['one'];
360 + }
380 361
381 - /**
382 - * Add secondary MultiSite host.
383 - *
384 - * @param $new_sites
385 - * An array with sites to add.
386 - *
387 - * @return void.
388 - */
389 - public static function addMultiSiteNewSites($new_sites)
390 - {
362 + return [];
363 + }
391 364
392 - }
365 + /**
366 + * Method to get firewall chart data.
367 + *
368 + * @param string $host_id
369 + * Host id on WebTotem.
370 + * @param int $days
371 + * For what period data is needed.
372 + *
373 + * @return array
374 + * Returns firewall chart data.
375 + */
376 + public static function getFirewallChart($host_id, $days = 7) {
377 + $period = WebTotem::getPeriod($days);
393 378
394 - /**
395 - * Get the date of creation of the site.
396 - *
397 - * @param string $site
398 - * The domain we want to check.
399 - *
400 - * @return string|bool
401 - * Returns host data.
402 - */
403 - public static function getGetSiteAddedDate($site)
404 - {
405 - if (!$site) {
406 - return FALSE;
407 - }
379 + $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'] . '} } }';
380 + $response = self::sendRequest($payload, TRUE);
408 381
409 - $hosts = self::getSites(1, 1, 'active', $site);
382 + if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
383 + return $response['data']['auth']['viewer']['sites']['one']['firewall'];
384 + }
410 385
411 - foreach ($hosts as $host) {
412 - if (!empty($host['created_at'])) {
413 - return $host['created_at'];
414 - }
415 - }
386 + return [];
387 + }
416 388
417 - return FALSE;
418 - }
389 + /**
390 + * Method to set firewall settings.
391 + *
392 + * @param string $host_id
393 + * Host id on WebTotem.
394 + * @param array $settings
395 + * User-specified settings.
396 + *
397 + * @return array
398 + * Returns information whether the request was successful.
399 + */
400 + public static function setFirewallSettings($host_id, array $settings) {
401 + $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 } } } } }"}';
402 + return self::sendRequest($payload, TRUE);
403 + }
419 404
420 - /**
421 - * Remove secondary MultiSite host.
422 - *
423 - * @param $host_id
424 - * Host id on WebTotem.
425 - *
426 - * @return bool
427 - * Returns result removing host.
428 - */
429 - public static function removeMultiSiteHost($host_id)
430 - {
405 + /**
406 + * Method to get antivirus data.
407 + *
408 + * @param array $params
409 + * Parameters for filtering data.
410 + *
411 + * @return array
412 + * Returns antivirus data.
413 + */
414 + public static function getAntivirus(array $params) {
431 415
432 - return false;
433 - }
416 + $cursor = ($params['cursor']) ? '"' . $params['cursor'] . '"' : 'null';
417 + $event = ($params['event']) ? '"' . $params['event'] . '"' : '"new"';
418 + $permissions = ($params['permissions']) ? ' "permissionsChanged":true, ' : '';
419 + $period = WebTotem::getPeriod($params['days']);
434 420
435 - /**
436 - * Method to get agents (AM, WAF, AV) statuses.
437 - *
438 - * @return array
439 - * Returns agents statuses data.
440 - */
441 - public static function getAgentsStatusesFromAPI()
442 - {
443 - $config_id = WebTotemOption::getOption('config_id');
444 - $response = self::sendRequest('/agents/' . $config_id . '/status', [], 'GET', TRUE);
421 + $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 __typename } } lastTest { time } stats { changed deleted scanned infected } } } } } } }"}';
422 + $response = self::sendRequest($payload, TRUE);
445 423
446 - if (isset($response['data'])) {
447 - return $response['data'];
448 - }
449 -
450 - return [];
424 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
425 + return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
451 426 }
427 + return [];
428 + }
452 429
453 - /**
454 - * Method for get monitoring data.
455 - *
456 - * @param string $host_id
457 - * Host id on WebTotem.
458 - * @param int|array $days
459 - * For what period data is needed.
460 - *
461 - * @return array
462 - * Returns all data.
463 - */
464 - public static function getMonitoringData($host_id)
465 - {
466 - $response = self::sendRequest('/dashboard/monitoring/' . $host_id . '/results', [], 'GET', TRUE);
430 + /**
431 + * Method to get antivirus last test.
432 + *
433 + * @param string $host_id
434 + * Host id on WebTotem.
435 + *
436 + * @return array
437 + * Returns antivirus last test data.
438 + */
439 + public static function getAntivirusLastTest($host_id) {
467 440
468 - if (isset($response['data'])) {
469 - return $response['data'];
470 - }
441 + $payload = '{"variables":{"id":"' . $host_id . '"},"query":"query ($id: ID!) { auth { viewer { sites { one(id: $id) { antivirus { status lastTest { time } } } } } } }"}';
442 + $response = self::sendRequest($payload, TRUE);
471 443
472 - return [];
444 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus'])) {
445 + return $response['data']['auth']['viewer']['sites']['one']['antivirus'];
473 446 }
447 + return [];
448 + }
474 449
475 - /**
476 - * Method to get firewall data.
477 - *
478 - * @param int $limit
479 - * Limit on the number of records.
480 - * @param string $page
481 - * Page for loading data.
482 - * @param int|array $days
483 - * For what period data is needed.
484 - *
485 - * @return array
486 - * Returns firewall data.
487 - */
488 - public static function getFirewall($limit = 20, $page = 1, $days = 365)
489 - {
490 - $period = WebTotem::getPeriod($days);
450 + /**
451 + * Method to force check services.
452 + *
453 + * @param string $host_id
454 + * Host id on WebTotem.
455 + * @param string $service
456 + * Service that needs to be checked.
457 + *
458 + * @return array
459 + * Returns information whether the request was successful.
460 + */
461 + public static function forceCheck($host_id, $service) {
462 + $payload = '{"variables":{"id":"' . $host_id . '","service":"' . $service . '"},"query":"mutation ($id: ID!, $service: ForceCheckService!) { auth { sites { forceCheck(siteId: $id, service: $service) } } }"} ';
463 + return self::sendRequest($payload, TRUE);
464 + }
491 465
492 - $config_id = WebTotemOption::getOption('config_id');
493 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/logs', [
494 - 'page_num' => $page,
495 - 'page_size' => $limit,
496 - 'from' => $period['from'],
497 - 'to' => $period['to']
498 - ], 'GET', TRUE);
466 + /**
467 + * Method to export antivirus report.
468 + *
469 + * @param string $host_id
470 + * Host id on WebTotem.
471 + * @param int|array $days
472 + * For what period data is needed.
473 + *
474 + * @return array
475 + * Returns information whether the request was successful.
476 + */
477 + public static function avExport($host_id, $days = 30) {
478 + $period = WebTotem::getPeriod($days);
479 + $payload = '{"variables":{ "input":{"siteId":"' . $host_id . '", "dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} }},"query":"mutation ($input: AvLogExportInput!) { auth { sites { av { export(input: $input) } } } }"} ';
480 + return self::sendRequest($payload, TRUE);
481 + }
499 482
483 + /**
484 + * Method to get quarantine data.
485 + *
486 + * @param string $host_id
487 + * Host id on WebTotem.
488 + *
489 + * @return array
490 + * Returns quarantine data.
491 + */
492 + public static function getQuarantineList($host_id) {
493 + $payload = '{"query":"query{ auth{ viewer{ sites{ one(id:\"' . $host_id . '\"){ antivirus{ quarantine{ id path date } } } } } } } "}';
494 + $response = self::sendRequest($payload, TRUE);
500 495
501 - if (isset($response['data'])) {
502 - return $response['data'];
503 - }
504 -
505 - return [];
496 + if (isset($response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'])) {
497 + return $response['data']['auth']['viewer']['sites']['one']['antivirus']['quarantine'];
506 498 }
499 + return [];
500 + }
507 501
508 - /**
509 - * Method to get firewall chart data.
510 - *
511 - * @param int $days
512 - * For what period data is needed.
513 - *
514 - * @return array
515 - * Returns firewall chart data.
516 - */
517 - public static function getFirewallStatistics($days = 7)
518 - {
519 - $period = WebTotem::getPeriod($days);
502 + /**
503 + * Method to move file to quarantine.
504 + *
505 + * @param string $host_id
506 + * Host id on WebTotem.
507 + * @param string $path
508 + * Path to the file.
509 + *
510 + * @return array
511 + * Returns information whether the request was successful.
512 + */
513 + public static function moveToQuarantine($host_id, $path) {
514 + $payload = '{"query":"mutation{ auth{ sites{ av{ moveToQuarantine(input:{ siteId:\"' . $host_id . '\", path:\"' . $path . '\" }) } } } } "}';
515 + return self::sendRequest($payload, TRUE);
516 + }
520 517
521 - $config_id = WebTotemOption::getOption('config_id');
522 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/statistics', [
523 - 'from' => $period['from'],
524 - 'to' => $period['to']
525 - ], 'GET', TRUE);
518 + /**
519 + * Method to move file from quarantine.
520 + *
521 + * @param string $id
522 + * Id assigned to the file.
523 + *
524 + * @return array
525 + * Returns information whether the request was successful.
526 + */
527 + public static function moveFromQuarantine($id) {
528 + $payload = '{"query":"mutation{ auth{ sites{ av{ moveFromQuarantine(id: \"' . $id . '\") } } } } "}';
529 + return self::sendRequest($payload, TRUE);
530 + }
526 531
532 + /**
533 + * Method to get server status data.
534 + *
535 + * @param string $host_id
536 + * Host id on WebTotem.
537 + * @param int|array $days
538 + * For what period data is needed.
539 + *
540 + * @return array
541 + * Returns server status data.
542 + */
543 + public static function getServerStatusData($host_id, $days = 7) {
544 + $period = WebTotem::getPeriod($days);
545 + $payload = '{ "query":"query($id: ID!, $dateRange: DateRangeInput!) { auth { viewer { sites { one(id: $id) { serverStatus { ramChart(dateRange: $dateRange){ total value time } cpuChart(dateRange: $dateRange){ value time } } } } } } }", "variables":{"id":"' . $host_id . '","dateRange":{"to":' . $period['to'] . ',"from":' . $period['from'] . '} } }';
527 546
528 - if (isset($response['data'])) {
529 - return $response['data'];
530 - }
547 + $response = self::sendRequest($payload, TRUE);
531 548
532 - return [];
549 + if (isset($response['data']['auth']['viewer']['sites']['one']['serverStatus'])) {
550 + return $response['data']['auth']['viewer']['sites']['one']['serverStatus'];
533 551 }
534 552
535 - /**
536 - * Method to get firewall settings.
537 - *
538 - * @return array
539 - * Returns information whether the request was successful.
540 - */
541 - public static function getFirewallSettings()
542 - {
543 - $config_id = WebTotemOption::getOption('config_id');
544 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/configs', [], 'GET', TRUE);
545 - if (isset($response['data'])) {
546 - return $response['data'];
547 - }
553 + return [];
554 + }
548 555
549 - return [];
550 - }
556 + /**
557 + * Method to remove port from ignore list.
558 + *
559 + * @param string $host_id
560 + * Host id on WebTotem.
561 + * @param string $port
562 + * User specified port.
563 + *
564 + * @return array
565 + * Returns information whether the request was successful.
566 + */
567 + public static function removeIgnorePort($host_id, $port) {
568 + $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . $port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { removeIgnorePort(input: $input) } } } }"} ';
569 + return self::sendRequest($payload, TRUE);
570 + }
551 571
552 - /**
553 - * Method to set firewall settings.
554 - *
555 - * @param array $settings
556 - * User-specified settings.
557 - *
558 - * @return array
559 - * Returns information whether the request was successful.
560 - */
561 - public static function setFirewallSettings(array $settings)
562 - {
563 - $config_id = WebTotemOption::getOption('config_id');
564 - return self::sendRequest('/dashboard/firewall/' . $config_id . '/configs', $settings, 'PATCH', TRUE);
565 - }
572 + /**
573 + * Method to add port to ignore list.
574 + *
575 + * @param string $host_id
576 + * Host id on WebTotem.
577 + * @param string $port
578 + * User specified port.
579 + *
580 + * @return array
581 + * Returns information whether the request was successful.
582 + */
583 + public static function addIgnorePort($host_id, $port) {
584 + $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "port":' . (int) $port . '} },"query":"mutation($input: IgnorePortInput!) { auth { sites { ps { addIgnorePort(input: $input) } } } }"} ';
585 + return self::sendRequest($payload, TRUE);
586 + }
566 587
588 + /**
589 + * Method to get all ports list.
590 + *
591 + * @param string $host_id
592 + * Host id on WebTotem.
593 + *
594 + * @return array
595 + * Returns ports data.
596 + */
597 + public static function getAllPortsList($host_id) {
598 + $payload = '{"query":"query($id: ID!) { auth { viewer { sites { one(id: $id) { ports { status ip tcp ignorePorts lastTest { time } } } } } } } ","variables":{"id":"' . $host_id . '"}}';
567 599
568 - /**
569 - * Method to get antivirus history data.
570 - *
571 - * @param int $page_num
572 - * Page number.
573 - * @param int $page_size
574 - * Number of entries per page.
575 - *
576 - * @return array
577 - * Returns antivirus history data.
578 - */
579 - public static function getAntivirusHistory($page_num = 1, $page_size = 10)
580 - {
581 - $config_id = WebTotemOption::getOption('config_id');
582 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/history', [
583 - 'page_num' => $page_num,
584 - 'page_size' => $page_size
585 - ], 'GET', TRUE);
600 + $response = self::sendRequest($payload, TRUE);
586 601
587 - if (isset($response['data'])) {
588 - return $response['data'];
589 - }
590 - return [];
602 + if (isset($response['data']['auth']['viewer']['sites']['one']['ports'])) {
603 + return $response['data']['auth']['viewer']['sites']['one']['ports'];
591 604 }
592 605
593 - /**
594 - * Method to get antivirus history details data.
595 - *
596 - * @param int $scan_id
597 - * Scan ID.
598 - * @param int $page_num
599 - * Page number.
600 - * @param int $page_size
601 - * Number of entries per page.
602 - *
603 - * @return array
604 - * Returns antivirus history data.
605 - */
606 - public static function getAntivirusHistoryDetails($scan_id, $page_num = 1, $page_size = 10)
607 - {
608 - $config_id = WebTotemOption::getOption('config_id');
609 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/history/' . $scan_id . '/details', [
610 - 'page_num' => $page_num,
611 - 'page_size' => $page_size
612 - ], 'GET', TRUE);
606 + return [];
607 + }
613 608
609 + /**
610 + * Method to get all reports.
611 + *
612 + * @param string $host_id
613 + * Host id on WebTotem.
614 + * @param int $limit
615 + * Limit on the number of records.
616 + * @param string $cursor
617 + * Mark for loading data.
618 + *
619 + * @return array
620 + * Returns reports data.
621 + */
622 + public static function getAllReports($host_id, $limit = 10, $cursor = NULL) {
623 + $cursor = ($cursor == NULL) ? 'null' : '"' . $cursor . '"';
624 + $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 } } } } } }"}';
625 + $response = self::sendRequest($payload, TRUE);
614 626
615 - if (isset($response['data'])) {
616 - return $response['data'];
617 - }
618 - return [];
627 + if (isset($response['data']['auth']['viewer']['reports']['list']['edges'])) {
628 + return $response['data']['auth']['viewer']['reports']['list'];
619 629 }
620 630
621 - /**
622 - * Method to get quarantine data.
623 - *
624 - * @param int $page_num
625 - * Page number.
626 - * @param int $page_size
627 - * Number of entries per page.
628 - *
629 - * @return array
630 - * Returns quarantine data.
631 - */
632 - public static function getAntivirusCurrentDetails($page_num = 1, $page_size = 5)
633 - {
634 - $config_id = WebTotemOption::getOption('config_id');
635 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/current/details', [
636 - 'page_num' => $page_num,
637 - 'page_size' => $page_size
638 - ], 'GET', TRUE);
631 + return [];
632 + }
639 633
640 - if (isset($response['data'])) {
641 - return $response['data'];
642 - }
643 - return [];
644 - }
634 + /**
635 + * Method to generate report.
636 + *
637 + * @param string $host_id
638 + * Host id on WebTotem.
639 + * @param int|array $days
640 + * For what period data is needed.
641 + * @param array $services
642 + * User-specified module settings.
643 + *
644 + * @return string|bool
645 + * Returns report download link.
646 + */
647 + public static function generateReport($host_id, $days, array $services) {
648 + $period = WebTotem::getPeriod($days);
649 + $language = WebTotem::getLanguage();
645 650
651 + $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 . '" } } }';
652 + $response = self::sendRequest($payload, TRUE);
646 653
647 - /**
648 - * Method to force check Antivirus.
649 - *
650 - * @return mixed
651 - * Returns information whether the request was successful.
652 - */
653 - public static function forceCheckAV()
654 - {
655 - $config_id = WebTotemOption::getOption('config_id');
656 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/check', [], 'POST', TRUE);
654 + if (isset($response['data']['auth']['viewer']['reports']['generate'])) {
655 + return $response['data']['auth']['viewer']['reports']['generate'];
657 656 }
658 657
658 + return FALSE;
659 + }
659 660
660 - /**
661 - * Method to force check services.
662 - *
663 - * @param string $host_id
664 - * Host id on WebTotem.
665 - * @param string $module_name
666 - * Service that needs to be checked.
667 - *
668 - * @return array
669 - * Returns information whether the request was successful.
670 - */
671 - public static function forceCheck($host_id, $module_name)
672 - {
673 - return self::sendRequest('/dashboard/hosts/' . $host_id . '/check', ['module_name' => $module_name], 'POST', TRUE);
661 + /**
662 + * Method to download report.
663 + *
664 + * @param string $id
665 + * Assigned to the report.
666 + *
667 + * @return string|bool
668 + * Returns report download link.
669 + */
670 + public static function downloadReport($id) {
671 + $payload = '{"query": "query { auth { viewer { reports { download(id: \"' . $id . '\") } } } }"}';
672 + $response = self::sendRequest($payload, TRUE);
673 +
674 + if (isset($response['data']['auth']['viewer']['reports']['download'])) {
675 + return $response['data']['auth']['viewer']['reports']['download'];
674 676 }
675 677
676 - /**
677 - * Method to get quarantine data.
678 - *
679 - * @param int $page_num
680 - * Page number.
681 - * @param int $page_size
682 - * Number of entries per page.
683 - *
684 - * @return array
685 - * Returns quarantine data.
686 - */
687 - public static function getQuarantineList($page_num = 1, $page_size = 5)
688 - {
689 - $config_id = WebTotemOption::getOption('config_id');
690 - $response = self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine', [
691 - 'page_num' => $page_num,
692 - 'page_size' => $page_size
693 - ], 'GET', TRUE);
678 + return FALSE;
679 + }
694 680
695 - if (isset($response['data'])) {
696 - return $response['data'];
697 - }
698 - return [];
681 + /**
682 + * Method to get configs data.
683 + *
684 + * @param string $host_id
685 + * Host id on WebTotem.
686 + *
687 + * @return array|bool
688 + * Returns configs data.
689 + */
690 + public static function getConfigs($host_id) {
691 + $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 } } } } } } } "}';
692 + $response = self::sendRequest($payload, TRUE);
693 +
694 + if (isset($response['data']['auth']['viewer']['sites']['one']['configs'])) {
695 + return $response['data']['auth']['viewer']['sites']['one']['configs'];
699 696 }
700 697
701 - /**
702 - * Method to move file to quarantine.
703 - *
704 - * @param string $file_id
705 - * File ID.
706 - *
707 - * @return array
708 - * Returns information whether the request was successful.
709 - */
710 - public static function moveToQuarantine($file_id)
711 - {
712 - $config_id = WebTotemOption::getOption('config_id');
713 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine/to-quarantine', [
714 - 'file_id' => $file_id,
715 - ], 'POST', TRUE);
716 - }
698 + return FALSE;
699 + }
717 700
718 - /**
719 - * Method to move file from quarantine.
720 - *
721 - * @param string $file_id
722 - * File ID.
723 - *
724 - * @return array
725 - * Returns information whether the request was successful.
726 - */
727 - public static function moveFromQuarantine($file_id)
728 - {
729 - $config_id = WebTotemOption::getOption('config_id');
730 - return self::sendRequest('/dashboard/antivirus/' . $config_id . '/quarantine/from-quarantine', [
731 - 'file_id' => $file_id,
732 - ], 'POST', TRUE);
701 + /**
702 + * Method to toggle modules config.
703 + *
704 + * @param string $service_id
705 + * Service id that we enable or disable.
706 + *
707 + * @return string|bool
708 + * Returns information whether the request was successful.
709 + */
710 + public static function toggleConfigs($service_id) {
711 + $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 } } } } } "}';
712 + $response = self::sendRequest($payload, TRUE);
713 +
714 + if (isset($response['data']['auth']['configs']['toggle'])) {
715 + return $response['data']['auth']['configs']['toggle'];
733 716 }
734 717
735 - /**
736 - * Method to get allow/deny ip list.
737 - *
738 - * @param string $type
739 - * Type of ip list
740 - *
741 - * @return array|bool
742 - * Returns ip allow/deny lists.
743 - */
744 - public static function getIpLists($type = 'blacklist')
745 - {
746 - $config_id = WebTotemOption::getOption('config_id');
747 - $response = self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist', [
748 - 'type' => $type,
749 - ], 'GET', TRUE);
718 + return FALSE;
719 + }
750 720
751 - if (isset($response['data'])) {
752 - return $response['data'];
753 - }
721 + /**
722 + * Method to toggle modules notification.
723 + *
724 + * @param string $host_id
725 + * Host id on WebTotem.
726 + * @param string $service
727 + * Service id in which we enable or disable notifications.
728 + *
729 + * @return string|bool
730 + * Returns information whether the request was successful.
731 + */
732 + public static function toggleNotifications($host_id, $service) {
733 + $payload = '{"query":"mutation{ auth{ sites{ toggleNotifications(siteId: \"' . $host_id . '\", service: ' . $service . ') } } }"}';
734 + $response = self::sendRequest($payload, TRUE);
754 735
755 - return [];
736 + if (isset($response['data']['auth']['sites']['toggleNotifications'])) {
737 + return $response;//['data']['auth']['sites']['toggleNotifications'];
756 738 }
757 739
758 - /**
759 - * Method to add ip to allow/deny list.
760 - *
761 - * @param string $ip
762 - * Ip address.
763 - * @param string $type
764 - * Allow or deny type.
765 - *
766 - * @return bool
767 - * Returns information whether the request was successful.
768 - */
769 - public static function addIpToList($ips, $type)
770 - {
771 - $config_id = WebTotemOption::getOption('config_id');
772 - self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist?type=' . $type, [
773 - 'ip' => array_filter($ips),
774 - ], 'POST', TRUE);
740 + return FALSE;
741 + }
775 742
776 - return true;
777 - }
743 + /**
744 + * Method to get allow/deny ip list.
745 + *
746 + * @param string $host_id
747 + * Host id on WebTotem.
748 + *
749 + * @return array|bool
750 + * Returns ip allow/deny lists.
751 + */
752 + public static function getIpLists($host_id) {
753 + $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 } } } } } } }"} ';
754 + $response = self::sendRequest($payload, TRUE);
778 755
779 - /**
780 - * Method to remove ip from allow/deny list by id.
781 - *
782 - * @param string $ip
783 - * Ip address.
784 - * @param string $type
785 - * Allow or deny type.
786 - *
787 - * @return bool
788 - * Returns information whether the request was successful.
789 - */
790 - public static function removeIpFromList($ip, $type)
791 - {
792 - $config_id = WebTotemOption::getOption('config_id');
793 - self::sendRequest('/dashboard/firewall/' . $config_id . '/configs/iplist?type=' . $type, [
794 - 'ip' => $ip,
795 - ], 'DELETE', TRUE);
796 -
797 - return true;
756 + if (isset($response['data']['auth']['viewer']['sites']['one']['firewall'])) {
757 + return $response['data']['auth']['viewer']['sites']['one']['firewall'];
798 758 }
799 759
800 - /**
801 - * Sends a REST API request to the WebTotem API server.
802 - *
803 - * @param string $endpoint
804 - * REST API endpoint (e.g., 'scan', 'status', etc.).
805 - * @param array $data
806 - * Associative array of data to send as JSON body or query parameters.
807 - * @param string $method
808 - * HTTP method: GET, POST, PUT, DELETE (default is POST).
809 - * @param bool $useToken
810 - * Whether to include the auth token.
811 - * @param bool $retry
812 - * Used to prevent recursion on token renewal.
813 - *
814 - * @return array|null
815 - * API response as an associative array, or null on failure.
816 - */
817 - protected static function sendRequest($endpoint, $data = [], $method = 'POST', $useToken = false, $retry = false, $silent = false)
818 - {
819 - self::$last_status = 0;
820 - $api_key = WebTotemOption::getOption('api_key');
760 + return [];
761 + }
821 762
822 - // Get or initialize the API URL.
823 - $api_url = WebTotemOption::getOption('api_url');
824 - if (!$api_url) {
825 - $api_url = self::getApiUrl();
826 - WebTotemOption::setOptions(['api_url' => $api_url]);
827 - }
763 + /**
764 + * Method to add ip to allow/deny list.
765 + *
766 + * @param string $host_id
767 + * Host id on WebTotem.
768 + * @param string $ips
769 + * Ip address list.
770 + * @param string $list
771 + * Allow or deny list.
772 + *
773 + * @return bool
774 + * Returns information whether the request was successful.
775 + */
776 + public static function addIpToList($host_id, $ips, $list) {
828 777
829 - // The API previously answered 429: respect the requested pause instead of
830 - // hammering the server (and getting the whole site throttled).
831 - $throttled_until = (int) WebTotemOption::getOption('api_retry_after');
832 - if ($throttled_until > time()) {
833 - self::notify($silent, 'warning', sprintf(
834 - /* translators: %d: number of seconds to wait. */
835 - __('Too many requests to the WebTotem API. Please try again in %d seconds.', 'wtotem'),
836 - $throttled_until - time()
837 - ));
778 + if ($ips) {
779 + $ips = WebTotem::convertIpListForApi($ips);
780 + $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "ips": ' . $ips . ', "color": "' . $list . '" } }, "query":"mutation($input: WafListInput!) { auth { sites { waf { addToList(input: $input){ status invalidIPs} } } } }"} ';
781 + $response = self::sendRequest($payload, TRUE);
838 782
839 - return NULL;
840 - }
783 + if (isset($response['data']['auth']['sites']['waf']['addToList'])) {
784 + return $response['data']['auth']['sites']['waf']['addToList'];
785 + }
786 + }
841 787
842 - $auth_token = NULL;
843 - if ($useToken) {
844 - $auth_token = WebTotemOption::getOption('auth_token');
845 - $auth_token_expired = (int) WebTotemOption::getOption('auth_token_expired');
788 + return FALSE;
789 + }
846 790
847 - if ((!$auth_token || $auth_token_expired <= time()) && !$retry) {
848 - $result = self::auth($api_key);
849 - if ($result === 'success') {
850 - return self::sendRequest($endpoint, $data, $method, $useToken, TRUE, $silent);
851 - }
852 - }
853 - }
791 + /**
792 + * Method to remove ip from allow/deny list by id.
793 + *
794 + * @param string $id
795 + * Id assignment to ip address.
796 + *
797 + * @return bool
798 + * Returns information whether the request was successful.
799 + */
800 + public static function removeIpFromList($id) {
801 + $payload = '{"variables":{ "id": "' . $id . '" },"query":"mutation($id: ID!) { auth { sites { waf { removeFromList(id: $id) } } } }"} ';
802 + $response = self::sendRequest($payload, TRUE);
854 803
855 - $url = rtrim($api_url, '/') . '/api/v1/' . ltrim($endpoint, '/');
804 + if (isset($response['data']['auth']['sites']['waf']['removeFromList'])) {
805 + return $response['data']['auth']['sites']['waf']['removeFromList'];
806 + }
856 807
857 - $args = [
858 - 'timeout' => 60,
859 - 'sslverify' => TRUE,
860 - 'headers' => [
861 - 'Accept' => 'application/json',
862 - 'Content-Type' => 'application/json',
863 - 'source' => 'WORDPRESS',
864 - ],
865 - ];
808 + return FALSE;
809 + }
866 810
867 - if ($auth_token) {
868 - $args['headers']['Authorization'] = "Bearer $auth_token";
869 - }
811 + /**
812 + * Method to get allow url list.
813 + *
814 + * @param string $host_id
815 + * Host id on WebTotem.
816 + *
817 + * @return array
818 + * Returns url allow lists.
819 + */
820 + public static function getAllowUrlList($host_id) {
821 + $payload = '{"query":"query { auth { viewer { sites { one(id: \"' . $host_id . '\"){ firewall{ urlWhiteList{ id url createdAt } } } } } } }"} ';
822 + $response = self::sendRequest($payload, TRUE);
870 823
871 - if (strtoupper($method) === 'GET') {
872 - $url = add_query_arg($data, $url);
873 - } else {
874 - $args['body'] = wp_json_encode($data);
875 - }
824 + if (isset($response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'])) {
825 + return $response['data']['auth']['viewer']['sites']['one']['firewall']['urlWhiteList'];
826 + }
876 827
877 - $response = wp_remote_request($url, array_merge($args, ['method' => strtoupper($method)]));
828 + return [];
829 + }
878 830
879 - if (is_wp_error($response)) {
880 - self::notify($silent, 'error', WebTotem::messageForHuman(
881 - 'SERVER UNAVAILABLE: ' . $response->get_error_message()
882 - ));
831 + /**
832 + * Method to add url to allow list.
833 + *
834 + * @param string $host_id
835 + * Host id on WebTotem.
836 + * @param string $url
837 + * User-specified url.
838 + *
839 + * @return bool|string
840 + * Returns information whether the request was successful.
841 + */
842 + public static function addUrlToAllowList($host_id, $url) {
843 + $payload = '{"variables":{ "input": { "siteId": "' . $host_id . '", "url": "' . $url . '" } }, "query":"mutation($input: WafUrlWhiteListInput!) { auth { sites { waf { addToUrlWhiteList(input: $input) } } } }"} ';
844 + $response = self::sendRequest($payload, TRUE);
883 845
884 - return NULL;
885 - }
846 + if (isset($response['data']['auth']['sites']['waf']['addToUrlWhiteList'])) {
847 + return $response['data']['auth']['sites']['waf']['addToUrlWhiteList'];
848 + }
886 849
887 - $code = (int) wp_remote_retrieve_response_code($response);
888 - self::$last_status = $code;
889 - $body = wp_remote_retrieve_body($response);
890 - $decoded = json_decode($body, TRUE);
850 + return FALSE;
851 + }
891 852
892 - if (!is_array($decoded)) {
893 - $decoded = [];
894 - }
853 + /**
854 + * Method to remove url from allow list.
855 + *
856 + * @param string $id
857 + * Id assignment to url address.
858 + *
859 + * @return bool|string
860 + * Returns information whether the request was successful.
861 + */
862 + public static function removeUrlFromAllowList($id) {
863 + $payload = '{"variables":{ "id": "' . $id . '" }, "query":"mutation($id: ID!) { auth { sites { waf { removeFromUrlWhiteList(id: $id) } } } }"} ';
864 + $response = self::sendRequest($payload, TRUE);
895 865
896 - $error_message = isset($decoded['message']) ? (string) $decoded['message'] : '';
866 + if (isset($response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'])) {
867 + return $response['data']['auth']['sites']['waf']['removeFromUrlWhiteList'];
868 + }
897 869
898 - // Terminal account states: show the dedicated page and stop.
899 - if ($error_message !== '') {
900 - if (stripos($error_message, 'Password expired') !== FALSE) {
901 - wtotem_error_page(['errors' => 'PASSWORD_EXPIRED']);
902 - exit();
903 - }
870 + return FALSE;
871 + }
904 872
905 - if (stripos($error_message, 'API_KEY_DEACTIVATED') !== FALSE) {
906 - wtotem_error_page(['errors' => 'TARIFF_EXPIRED']);
907 - exit();
908 - }
909 - }
873 + /**
874 + * Method to get user's email.
875 + *
876 + * @return string
877 + * Returns user's email.
878 + */
879 + public static function getEmail(){
880 + $payload = '{"query":"query { auth { viewer { email __typename } __typename } }"}';
881 + $response = self::sendRequest($payload, true);
910 882
911 - // 429 Too Many Requests: honour Retry-After and never re-authorize.
912 - if ($code === 429) {
913 - $delay = self::parseRetryAfter(wp_remote_retrieve_header($response, 'retry-after'));
914 - WebTotemOption::setOptions(['api_retry_after' => time() + $delay]);
915 - self::notify($silent, 'warning', sprintf(
916 - /* translators: %d: number of seconds to wait. */
917 - __('Too many requests to the WebTotem API. Please try again in %d seconds.', 'wtotem'),
918 - $delay
919 - ));
883 + return $response['data']['auth']['viewer']['email'];
884 + }
920 885
921 - return NULL;
922 - }
886 + /**
887 + * Function sends GraphQL request to API server.
888 + *
889 + * @param string $payload
890 + * Payload to be sent to API server.
891 + * @param bool $token
892 + * Whether a token is needed when sending a request.
893 + * @param bool $repeat
894 + * Required to avoid recursion.
895 + *
896 + * @return array
897 + * Returns response from WebTotem API.
898 + */
899 + protected static function sendRequest($payload, $token = FALSE, $repeat = FALSE) {
923 900
924 - // 401 Unauthorized: the token is gone or expired. Re-login by API key once.
925 - if ($code === 401) {
926 - if (!$retry && $api_key && self::auth($api_key, TRUE) === 'success') {
927 - return self::sendRequest($endpoint, $data, $method, $useToken, TRUE, $silent);
928 - }
901 + $api_key = WebTotemOption::getOption('api_key');
929 902
930 - self::notify($silent, 'error', WebTotem::messageForHuman(
931 - $error_message !== '' ? $error_message : 'invalid credentials'
932 - ));
903 + // Remote URL where the public WebTotem API service is running.
904 + $api_url = WebTotemOption::getOption('api_url');
905 + if(!$api_url){
906 + $api_url = self::getApiUrl('P');
907 + WebTotemOption::setOptions(['api_url' => $api_url]);
908 + }
933 909
934 - return $decoded;
935 - }
910 + // Checking whether a token is needed.
911 + if ($token) {
912 + $auth_token = WebTotemOption::getOption('auth_token');
913 + $auth_token_expired = WebTotemOption::getOption('auth_token_expired');
936 914
937 - // 403 Forbidden: authenticated, but not allowed. Re-login would not help.
938 - if ($code === 403) {
939 - if (stripos($error_message, 'USERHOST_NOT_BELONG_TO_USER') !== FALSE) {
940 - self::forgetHost();
941 - } else {
942 - self::notify($silent, 'error', WebTotem::messageForHuman(
943 - $error_message !== '' ? $error_message : 'access denied'
944 - ));
945 - }
946 -
947 - return $decoded;
915 + // Checking whether the token has expired.
916 + if ($auth_token_expired <= time() && !$repeat) {
917 + $result = self::auth($api_key);
918 + if ($result === 'success') {
919 + return self::sendRequest($payload, $token, TRUE);
948 920 }
949 -
950 - // Older API builds answer 200 with an error message in the body.
951 - if ($error_message !== '') {
952 - if ($error_message === 'invalid credentials') {
953 - if (!$retry && $api_key && self::auth($api_key, TRUE) === 'success') {
954 - return self::sendRequest($endpoint, $data, $method, $useToken, TRUE, $silent);
955 - }
956 - } elseif (stripos($error_message, 'USERHOST_NOT_BELONG_TO_USER') !== FALSE) {
957 - self::forgetHost();
958 - } else {
959 - self::notify($silent, 'error', WebTotem::messageForHuman($error_message));
960 - }
921 + else {
922 + if(isset($result['errors'])){
923 + $message = WebTotem::messageForHuman($result['errors'][0]['message']);
924 + WebTotemOption::setNotification('error', $message);
925 + }
961 926 }
927 + }
928 + }
962 929
963 - return $decoded;
964 - }
930 + if (function_exists('wp_remote_post')) {
965 931
966 - /**
967 - * Reads the Retry-After header into a number of seconds.
968 - *
969 - * The header is either a number of seconds or an HTTP date.
970 - *
971 - * @param string $header
972 - * Raw Retry-After header value.
973 - *
974 - * @return int
975 - * Seconds to wait, clamped to a sane range.
976 - */
977 - protected static function parseRetryAfter($header)
978 - {
979 - $delay = 0;
980 - $header = is_string($header) ? trim($header) : '';
932 + $args = [
933 + 'body' => $payload,
934 + 'timeout' => '60',
935 + 'sslverify' => false,
936 + 'headers' => [
937 + 'Content-Type:application/json',
938 + 'Content-Type' => 'application/json',
939 + 'Accept: application/json',
940 + 'source: WORDPRESS',
941 + ],
942 + ];
981 943
982 - if ($header !== '') {
983 - if (ctype_digit($header)) {
984 - $delay = (int) $header;
985 - } else {
986 - $timestamp = strtotime($header);
987 - if ($timestamp !== FALSE) {
988 - $delay = $timestamp - time();
989 - }
990 - }
991 - }
944 + if (isset($auth_token)) {
945 + $auth = "Bearer " . $auth_token;
946 + $args['headers'] = array_merge($args['headers'], ["Authorization" => $auth]);
947 + }
992 948
993 - if ($delay < 1) {
994 - $delay = 60;
995 - }
949 + $response = wp_remote_post($api_url, $args);
950 + $response = wp_remote_retrieve_body($response);
951 + $response = json_decode($response, true);
996 952
997 - // Never park the plugin for longer than an hour.
998 - return min($delay, HOUR_IN_SECONDS);
999 953 }
954 + else {
955 + $error = 'WP_REMOTE_POST_NOT_EXIST';
956 + }
1000 957
1001 - /**
1002 - * Drops the locally stored host binding after the API disowned it.
1003 - *
1004 - * @return void
1005 - */
1006 - protected static function forgetHost()
1007 - {
1008 - if (WebTotem::isMultiSite()) {
1009 - WebTotemOption::clearAllHosts();
958 + // Checking if there are errors in the response.
959 + if (isset($response['errors'][0]['message'])) {
960 + $message = WebTotem::messageForHuman($response['errors'][0]['message']);
961 + if (stripos($response['errors'][0]['message'], "INVALID_TOKEN") !== FALSE && !$repeat) {
962 + $response = self::auth($api_key);
963 + if ($response === 'success') {
964 + return self::sendRequest($payload, $token, TRUE);
1010 965 }
966 + }
967 + elseif(stripos($response['errors'][0]['message'], "USERHOST_NOT_BELONG_TO_USER") !== FALSE){
968 + if(WebTotem::isMultiSite()){
969 + WebTotemOption::clearAllHosts();
970 + WebTotemOption::clearOptions([ 'host_id', 'host_name' ]);
971 + } else {
972 + WebTotemOption::clearOptions([ 'host_id', 'host_name' ]);
973 + }
974 + }
975 + else {
976 + WebTotemOption::setNotification('error', $message);
977 + }
978 + }
1011 979
1012 - WebTotemOption::clearOptions(['host_id', 'host_name']);
980 + if (!empty($error)) {
981 + WebTotemOption::setNotification('error', $error);
1013 982 }
983 +
984 + return $response;
985 + }
1014 986
1015 987 }