PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.18
Patchstack – WordPress & Plugins Security v2.1.18
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / api.php

api.php in Patchstack – WordPress & Plugins Security 2.1.18, at includes/api.php

337 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used as a base for communicating with the Patchstack API.
10 */
11 class P_Api extends P_Core {
12
13 /**
14 * @var integer The current blog id.
15 */
16 public $blog_id;
17
18 /**
19 * Add the actions required for the API.
20 *
21 * @param Patchstack $core
22 * @return void
23 */
24 public function __construct( $core ) {
25 parent::__construct( $core );
26 $this->blog_id = get_current_blog_id();
27 add_action( 'patchstack_update_license_status', array( $this, 'update_license_status' ) );
28 add_action( 'patchstack_send_ping', array( $this, 'ping' ) );
29 }
30
31 /**
32 * Get the API token.
33 *
34 * @param string $clientid The API client ID.
35 * @param string $secretkey The API secret key.
36 * @param boolean $fresh Whether or not to get a fresh token.
37 * @return null|string
38 */
39 public function get_access_token( $clientid = '', $secretkey = '', $fresh = false ) {
40 // Get current access token, if it exists.
41 $token_data = $this->get_blog_option( $this->blog_id, 'patchstack_api_token', false );
42
43 // If we do not need a fresh token, get the current one if it's not expired.
44 if ( ! $fresh && isset( $token_data['token'] ) && ! $this->has_expired( $token_data['expiresin'] ) ) {
45 return $token_data['token'];
46 }
47
48 // Call API and get the new access token.
49 $response = $this->fetch_access_token( $clientid, $secretkey );
50 if ( $response && $response->result == 'success' ) {
51 $this->update_blog_option(
52 $this->blog_id,
53 'patchstack_api_token',
54 array(
55 'token' => $response->message,
56 'expiresin' => $response->expiresin,
57 )
58 );
59 return $response->message;
60 }
61
62 // If we reach this, it means we were not able to get the access token.
63 $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' );
64 return null;
65 }
66
67 /**
68 * Fetch the API Token from API Server.
69 *
70 * @param string $clientid The API client ID.
71 * @param string $secretkey The API secret key.
72 * @return string|array
73 */
74 public function fetch_access_token( $clientid = '', $secretkey = '' ) {
75 // Skeleton for the response data.
76 $response_data = (object) array(
77 'result' => '',
78 'message' => '',
79 'expiresin' => '',
80 );
81
82 // Determine if the license id/key is set.
83 $client_id = $this->get_blog_option( $this->blog_id, 'patchstack_clientid', false ) ? $this->get_blog_option( $this->blog_id, 'patchstack_clientid', false ) : $clientid;
84 $client_secret = $this->get_blog_option( $this->blog_id, 'patchstack_secretkey', false ) ? $this->get_blog_option( $this->blog_id, 'patchstack_secretkey', false ) : $secretkey;
85 if ( empty( $client_id ) || empty( $client_secret ) ) {
86 $response_data->result = 'failed';
87 $response_data->message = __( 'API keys missing! Unable to obtain an access token.', 'patchstack' );
88 return $response_data;
89 }
90
91 // Send a request to our server to obtain the access token.
92 $response = wp_remote_post(
93 $this->plugin->auth_url . '/oauth/token',
94 array(
95 'method' => 'POST',
96 'timeout' => 60,
97 'redirection' => 5,
98 'httpversion' => '1.0',
99 'blocking' => true,
100 'headers' => array(),
101 'body' => array(
102 'client_id' => $client_id,
103 'client_secret' => $client_secret,
104 'grant_type' => 'client_credentials',
105 ),
106 'cookies' => array(),
107 )
108 );
109
110 // Stop if we received an error from the API.
111 if ( is_wp_error( $response ) ) {
112 $response_data->result = 'failed';
113 $response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $response->get_error_message();
114 return $response_data;
115 }
116
117 // Parse the result.
118 $result = json_decode( wp_remote_retrieve_body( $response ) );
119 if ( isset( $result->access_token ) ) {
120 $response_data->result = 'success';
121 $response_data->message = $result->access_token;
122 $response_data->expiresin = $result->expires_in;
123
124 // We need to know when the token expires.
125 // Defer to 'expires' if it is provided instead.
126 if ( isset( $result->expires_in ) ) {
127 if ( ! is_numeric( $result->expires_in ) ) {
128 $response_data->message = 'expires_in value must be an integer';
129 return $response_data;
130 }
131 $response_data->expiresin = $result->expires_in != 0 ? time() + $result->expires_in : 0;
132 } elseif ( ! empty( $result->expires_in ) ) {
133 // Some providers supply the seconds until expiration rather than
134 // the exact timestamp. Take a best guess at which we received.
135 $expires = $options['expires'];
136 if ( ! $this->isExpirationTimestamp( $expires ) ) {
137 $expires += time();
138 }
139 $response_data->expiresin = $expires;
140 }
141 return $response_data;
142 } elseif ( isset( $result->error ) ) {
143 $response_data->result = $result->error;
144 $response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $result->message;
145 return $response_data;
146 }
147 }
148
149 /**
150 * Checks if the API token has expired.
151 *
152 * @param integer $expiresin API token expiry.
153 * @return boolean If the token has expired.
154 */
155 public function has_expired( $expiresin ) {
156 return ( $expiresin < ( time() + 30 ) );
157 }
158
159 /**
160 * Retrieve the status of a license.
161 *
162 * @return void|array
163 */
164 public function update_license_status() {
165 // Get current license status.
166 $response = $this->send_request( '/api/license/verify', 'GET' );
167
168 // Update the representing options.
169 if ( isset( $response['expires_at'] ) ) {
170 $this->update_blog_option( $this->blog_id, 'patchstack_license_expiry', $response['expires_at'] );
171 }
172
173 if ( isset( $response['free'] ) ) {
174 $this->update_blog_option( $this->blog_id, 'patchstack_license_free', $response['free'] == false ? 0 : 1 );
175
176 if ( $response['free'] == true ) {
177 $this->update_blog_option( $this->blog_id, 'patchstack_show_settings', 0 );
178 }
179 }
180
181 if ( isset( $response['active'] ) && $response['active'] == true ) {
182 $this->update_blog_option( $this->blog_id, 'patchstack_license_activated', true );
183 }
184
185 return $response;
186 }
187
188 /**
189 * Send a request to the API with optionally POST data.
190 *
191 * @param string $url
192 * @param string $request
193 * @param array $data
194 * @return void|array If successful array, otherwise void.
195 */
196 public function send_request( $url, $request, $data = array() ) {
197 // Attempt to get the access token.
198 $token = $this->get_access_token();
199 if ( empty( $token ) ) {
200 return;
201 }
202
203 // Send the remote request using the WordPress built-in method.
204 $response = wp_remote_request(
205 $this->plugin->api_url . $url,
206 array(
207 'method' => $request,
208 'timeout' => 60,
209 'redirection' => 5,
210 'httpversion' => '1.0',
211 'blocking' => true,
212 'headers' => array(
213 'Authorization' => 'Bearer ' . $token,
214 'LicenseID' => $this->get_blog_option( $this->blog_id, 'patchstack_clientid', 0 ),
215 'Source-Host' => get_site_url(),
216 ),
217 'body' => $data,
218 'cookies' => array(),
219 )
220 );
221
222 // Check error or status code.
223 if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) {
224 $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' );
225 return;
226 }
227
228 return json_decode( wp_remote_retrieve_body( $response ), true );
229 }
230
231 /**
232 * Get the firewall rules.
233 *
234 * @return array The firewall rules.
235 */
236 public function post_firewall_rule_json() {
237 // If the request is coming from the API, fetch fresh rules.
238 if ( isset( $_POST['webarx_refresh_rules'] ) ) {
239 return $this->send_request( '/api/get-rules/2?bypass=cache', 'POST' );
240 }
241
242 return $this->send_request( '/api/get-rules/2', 'POST' );
243 }
244
245 /**
246 * Get the .htaccess rules.
247 *
248 * @param array $settings The settings on which .htaccess rules to get.
249 * @return array The .htaccess rules.
250 */
251 public function post_firewall_rule( $settings ) {
252 return $this->send_request( '/api/rules', 'POST', $settings );
253 }
254
255 /**
256 * Get the .htaccess firewall rules.
257 *
258 * @return array The .htaccess rules.
259 */
260 public function post_firewall_htaccess_rule() {
261 return $this->send_request( '/api/rules/htaccess', 'POST' );
262 }
263
264 /**
265 * Send the firewall logs to the API.
266 *
267 * @param array $logs
268 * @return array
269 */
270 public function upload_firewall_logs( $logs ) {
271 return $this->send_request( '/api/logs/log', 'POST', $logs );
272 }
273
274 /**
275 * Send the activity logs to the server.
276 *
277 * @param array $logs
278 * @return array
279 */
280 public function upload_activity_logs( $logs ) {
281 return $this->send_request( '/api/activity/log', 'POST', $logs );
282 }
283
284 /**
285 * Send WordPress core, theme, plugins versions and information to the API.
286 *
287 * @param array $software
288 * @return array
289 */
290 public function upload_software( $software ) {
291 return $this->send_request( '/api/sw/json', 'POST', $software );
292 }
293
294 /**
295 * Update the firewall status.
296 *
297 * @param array $status
298 * @return array
299 */
300 public function update_firewall_status( $status ) {
301 if ( $this->get_option( 'patchstack_license_free', 0 ) == 1 ) {
302 return;
303 }
304
305 return $this->send_request( '/api/firewall/update/status', 'POST', $status );
306 }
307
308 /**
309 * Update the URL on the API.
310 *
311 * @param array $url The current URL of the site.
312 * @return array
313 */
314 public function update_url( $url ) {
315 return $this->send_request( '/api/plugin/update/url', 'POST', $url );
316 }
317
318 /**
319 * Send list of sites and get the id and secret key in response.
320 *
321 * @param array $sites
322 * @return array
323 */
324 public function get_site_licenses( $sites ) {
325 return $this->send_request( '/api/multisite-keys', 'POST', $sites );
326 }
327
328 /**
329 * Send a ping to the Patchstack API every 3 hours to make sure that the plugin is still running.
330 *
331 * @return void
332 */
333 public function ping() {
334 $this->send_request( '/api/ping', 'POST', array( 'firewall' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ? 1 : 0 ) );
335 }
336 }
337