| @@ -15,13 +15,8 @@ | ||
| 15 | 15 | */ |
| 16 | 16 | public $blog_id; |
| 17 | 17 | |
| 18 | 18 | /** |
| 19 | - * @var string Error message from the API. | |
| 20 | - */ | |
| 21 | - public $message; | |
| 22 | - | |
| 23 | - /** | |
| 24 | 19 | * Add the actions required for the API. |
| 25 | 20 | * |
| 26 | 21 | * @param Patchstack $core |
| 27 | 22 | * @return void |
| @@ -30,9 +25,8 @@ | ||
| 30 | 25 | parent::__construct( $core ); |
| 31 | 26 | $this->blog_id = get_current_blog_id(); |
| 32 | 27 | add_action( 'patchstack_update_license_status', [ $this, 'update_license_status' ] ); |
| 33 | 28 | add_action( 'patchstack_send_ping', [ $this, 'ping' ] ); |
| 34 | - add_action( 'patchstack_send_header_request', [ $this, 'send_header_request' ] ); | |
| 35 | 29 | } |
| 36 | 30 | |
| 37 | 31 | /** |
| 38 | 32 | * Get the API token. |
| @@ -65,9 +59,8 @@ | ||
| 65 | 59 | return $response->message; |
| 66 | 60 | } |
| 67 | 61 | |
| 68 | 62 | // If we reach this, it means we were not able to get the access token. |
| 69 | - $this->message = $response; | |
| 70 | 63 | $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' ); |
| 71 | 64 | return null; |
| 72 | 65 | } |
| 73 | 66 | |
| @@ -75,9 +68,9 @@ | ||
| 75 | 68 | * Fetch the API Token from API Server. |
| 76 | 69 | * |
| 77 | 70 | * @param string $clientid The API client ID. |
| 78 | 71 | * @param string $secretkey The API secret key. |
| 79 | - * @return string|array|object | |
| 72 | + * @return string|array | |
| 80 | 73 | */ |
| 81 | 74 | public function fetch_access_token( $clientid = '', $secretkey = '' ) { |
| 82 | 75 | // Skeleton for the response data. |
| 83 | 76 | $response_data = (object) [ |
| @@ -98,9 +91,9 @@ | ||
| 98 | 91 | |
| 99 | 92 | // Make sure these values are set. |
| 100 | 93 | if ( empty( $client_id ) || empty( $client_secret ) ) { |
| 101 | 94 | $response_data->result = 'failed'; |
| 102 | - $response_data->message = esc_attr__( 'API keys missing! Unable to obtain an access token.', 'patchstack' ); | |
| 95 | + $response_data->message = __( 'API keys missing! Unable to obtain an access token.', 'patchstack' ); | |
| 103 | 96 | return $response_data; |
| 104 | 97 | } |
| 105 | 98 | |
| 106 | 99 | // Send a request to our server to obtain the access token. |
| @@ -122,21 +115,11 @@ | ||
| 122 | 115 | ] |
| 123 | 116 | ); |
| 124 | 117 | |
| 125 | 118 | // Stop if we received an error from the API. |
| 126 | - if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) == 401 ) { | |
| 127 | - $this->message = wp_remote_retrieve_body( $response ); | |
| 128 | - | |
| 129 | - if ( wp_remote_retrieve_response_code( $response ) == 401 ) { | |
| 130 | - $this->update_blog_option( $this->blog_id, 'patchstack_clientid', '' ); | |
| 131 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey', '' ); | |
| 132 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey_nonce', '' ); | |
| 133 | - $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' ); | |
| 134 | - } | |
| 135 | - | |
| 119 | + if ( is_wp_error( $response ) ) { | |
| 136 | 120 | $response_data->result = 'failed'; |
| 137 | - $response_data->message = esc_attr__( 'Unexpected error! Unable to obtain an access token. Error code: ', 'patchstack' ) . wp_remote_retrieve_response_code( $response ); | |
| 138 | - $response_data->body = $this->message; | |
| 121 | + $response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $response->get_error_message(); | |
| 139 | 122 | return $response_data; |
| 140 | 123 | } |
| 141 | 124 | |
| 142 | 125 | // Parse the result. |
| @@ -149,9 +132,8 @@ | ||
| 149 | 132 | // We need to know when the token expires. |
| 150 | 133 | // Defer to 'expires' if it is provided instead. |
| 151 | 134 | if ( isset( $result->expires_in ) ) { |
| 152 | 135 | if ( ! is_numeric( $result->expires_in ) ) { |
| 153 | - $response_data->result = 'failed'; | |
| 154 | 136 | $response_data->message = 'expires_in value must be an integer'; |
| 155 | 137 | return $response_data; |
| 156 | 138 | } |
| 157 | 139 | $response_data->expiresin = $result->expires_in != 0 ? time() + $result->expires_in : 0; |
| @@ -159,76 +141,14 @@ | ||
| 159 | 141 | |
| 160 | 142 | return $response_data; |
| 161 | 143 | } elseif ( isset( $result->error ) ) { |
| 162 | 144 | $response_data->result = $result->error; |
| 163 | - $response_data->message = esc_attr__( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $result->message; | |
| 145 | + $response_data->message = __( 'Unexpected error! Unable to obtain an access token.', 'patchstack' ) . $result->message; | |
| 164 | 146 | return $response_data; |
| 165 | 147 | } |
| 166 | 148 | } |
| 167 | 149 | |
| 168 | 150 | /** |
| 169 | - * Send a request to the API with optionally POST data. | |
| 170 | - * | |
| 171 | - * @param string $url | |
| 172 | - * @param string $method | |
| 173 | - * @param array $data | |
| 174 | - * @return void|array If successful array, otherwise void. | |
| 175 | - */ | |
| 176 | - public function send_request( $url, $method, $data = [] ) { | |
| 177 | - // Attempt to get the access token. | |
| 178 | - $token = $this->get_access_token(); | |
| 179 | - if ( empty( $token ) ) { | |
| 180 | - return; | |
| 181 | - } | |
| 182 | - | |
| 183 | - // Pass the multisite value to all requests, only for POST requests. | |
| 184 | - if ( $method == 'POST' ) { | |
| 185 | - $data['is_multisite'] = $this->is_multi_site ? 1 : 0; | |
| 186 | - } | |
| 187 | - | |
| 188 | - // Send the remote request using the WordPress built-in method. | |
| 189 | - $response = wp_remote_request( | |
| 190 | - $this->plugin->api_url . $url, | |
| 191 | - [ | |
| 192 | - 'method' => $method, | |
| 193 | - 'timeout' => 60, | |
| 194 | - 'redirection' => 5, | |
| 195 | - 'httpversion' => '1.0', | |
| 196 | - 'blocking' => true, | |
| 197 | - 'headers' => [ | |
| 198 | - 'Authorization' => 'Bearer ' . $token, | |
| 199 | - 'LicenseID' => $this->get_blog_option( $this->blog_id, 'patchstack_clientid', 0 ), | |
| 200 | - 'Source-Host' => get_site_url(), | |
| 201 | - ], | |
| 202 | - 'body' => $data, | |
| 203 | - 'cookies' => [], | |
| 204 | - ] | |
| 205 | - ); | |
| 206 | - | |
| 207 | - // Check error or status code. | |
| 208 | - if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) { | |
| 209 | - | |
| 210 | - // See if we received a site API connection termination. | |
| 211 | - $body = json_decode( wp_remote_retrieve_body( $response ), true ); | |
| 212 | - if ( isset( $body['cancel'] ) ) { | |
| 213 | - $this->update_blog_option( $this->blog_id, 'patchstack_clientid', '' ); | |
| 214 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey', '' ); | |
| 215 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey_nonce', '' ); | |
| 216 | - $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' ); | |
| 217 | - } | |
| 218 | - | |
| 219 | - return wp_remote_retrieve_response_code( $response ); | |
| 220 | - } | |
| 221 | - | |
| 222 | - // A 200 OK means we successfully communicated with the API for this sync | |
| 223 | - // action (license verify, log/software upload, rule pull, ping, etc.), so | |
| 224 | - // record it as the last successful sync time. | |
| 225 | - $this->update_blog_option( $this->blog_id, 'patchstack_last_sync', time() ); | |
| 226 | - | |
| 227 | - return json_decode( wp_remote_retrieve_body( $response ), true ); | |
| 228 | - } | |
| 229 | - | |
| 230 | - /** | |
| 231 | 151 | * Checks if the API token has expired. |
| 232 | 152 | * |
| 233 | 153 | * @param integer $expiresin API token expiry. |
| 234 | 154 | * @return boolean If the token has expired. |
| @@ -233,12 +153,8 @@ | ||
| 233 | 153 | * @param integer $expiresin API token expiry. |
| 234 | 154 | * @return boolean If the token has expired. |
| 235 | 155 | */ |
| 236 | 156 | public function has_expired( $expiresin ) { |
| 237 | - // A stored expiry of 0 means the token never expires. | |
| 238 | - if ( $expiresin === 0 ) { | |
| 239 | - return false; | |
| 240 | - } | |
| 241 | 157 | return ( $expiresin < ( time() + 30 ) ); |
| 242 | 158 | } |
| 243 | 159 | |
| 244 | 160 | /** |
| @@ -243,97 +159,102 @@ | ||
| 243 | 159 | |
| 244 | 160 | /** |
| 245 | 161 | * Retrieve the status of a license. |
| 246 | 162 | * |
| 247 | - * @param boolean $fetchPolicy Whether or not to fetch the policy settings. | |
| 248 | 163 | * @return void|array |
| 249 | 164 | */ |
| 250 | - public function update_license_status($fetchPolicy = false) { | |
| 165 | + public function update_license_status() { | |
| 251 | 166 | // Get current license status. |
| 252 | - $response = $this->send_request( '/api/license/verify' . ($fetchPolicy ? '?fetchPolicy=true' : ''), 'GET' ); | |
| 167 | + $response = $this->send_request( '/api/license/verify', 'GET' ); | |
| 253 | 168 | |
| 254 | - // Invalid license, or no longer active. | |
| 255 | - if ( ! is_array( $response ) && $response == 422 ) { | |
| 256 | - $this->update_blog_option( $this->blog_id, 'patchstack_clientid', '' ); | |
| 257 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey', '' ); | |
| 258 | - $this->update_blog_option( $this->blog_id, 'patchstack_secretkey_nonce', '' ); | |
| 259 | - $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' ); | |
| 260 | - return; | |
| 261 | - } | |
| 262 | - | |
| 263 | 169 | // Update the representing options. |
| 264 | - // Expiry date. | |
| 265 | 170 | if ( isset( $response['expires_at'] ) ) { |
| 266 | 171 | $this->update_blog_option( $this->blog_id, 'patchstack_license_expiry', $response['expires_at'] ); |
| 267 | 172 | } |
| 268 | 173 | |
| 269 | - // Free vs Paid license. | |
| 270 | 174 | if ( isset( $response['free'] ) ) { |
| 271 | 175 | $this->update_blog_option( $this->blog_id, 'patchstack_license_free', $response['free'] == false ? 0 : 1 ); |
| 272 | 176 | |
| 273 | 177 | if ( $response['free'] == true ) { |
| 274 | 178 | $this->update_blog_option( $this->blog_id, 'patchstack_show_settings', 0 ); |
| 275 | - $this->update_blog_option( $this->blog_id, 'patchstack_firewall_rules_v3', '[]' ); | |
| 276 | 179 | } else { |
| 277 | 180 | $this->send_header_request(); |
| 278 | 181 | } |
| 279 | 182 | } |
| 280 | 183 | |
| 281 | - // Active subscription. | |
| 282 | - if ( isset( $response['active'] ) ) { | |
| 283 | - $this->update_blog_option( $this->blog_id, 'patchstack_license_activated', $response['active'] == true ? 1 : 0 ); | |
| 184 | + if ( isset( $response['active'] ) && $response['active'] == true ) { | |
| 185 | + $this->update_blog_option( $this->blog_id, 'patchstack_license_activated', true ); | |
| 284 | 186 | } |
| 285 | 187 | |
| 286 | - // Subscription class. | |
| 287 | 188 | if ( isset( $response['class'] ) ) { |
| 288 | 189 | $this->update_blog_option( $this->blog_id, 'patchstack_subscription_class', $response['class'] ); |
| 289 | 190 | $this->update_blog_option( $this->blog_id, 'patchstack_last_license_check', time() ); |
| 290 | 191 | } |
| 291 | 192 | |
| 292 | - // Managed site status. | |
| 293 | 193 | if ( isset( $response['managed'], $response['managed_string'] ) ) { |
| 294 | - $this->update_blog_option( $this->blog_id, 'patchstack_managed', $response['managed'] ? 1 : 0 ); | |
| 194 | + $this->update_blog_option( $this->blog_id, 'patchstack_managed', $response['managed'] ); | |
| 295 | 195 | $this->update_blog_option( $this->blog_id, 'patchstack_managed_text', $response['managed_string'] ); |
| 296 | 196 | } |
| 297 | 197 | |
| 298 | - // Site ID. | |
| 299 | 198 | if ( isset( $response['site_id'] ) ) { |
| 300 | 199 | $this->update_blog_option( $this->blog_id, 'patchstack_site_id', $response['site_id'] ); |
| 301 | 200 | } |
| 302 | 201 | |
| 303 | - // Policy settings. | |
| 304 | - if ( isset( $response['policy'] ) && is_array( $response['policy'] ) && count( $response['policy'] ) > 0 ) { | |
| 305 | - foreach ( $response['policy'] as $key => $value ) { | |
| 306 | - // Make sure the option exists. | |
| 307 | - if ( ! array_key_exists( $key, $this->plugin->admin_options->options ) ) { | |
| 308 | - continue; | |
| 309 | - } | |
| 202 | + return $response; | |
| 203 | + } | |
| 310 | 204 | |
| 311 | - // Booleans would persist as '1' / '' otherwise, store them as 1/0 so type checks behave consistently. | |
| 312 | - if ( is_bool( $value ) ) { | |
| 313 | - $value = $value ? 1 : 0; | |
| 314 | - } | |
| 205 | + /** | |
| 206 | + * Send a request to the API with optionally POST data. | |
| 207 | + * | |
| 208 | + * @param string $url | |
| 209 | + * @param string $request | |
| 210 | + * @param array $data | |
| 211 | + * @return void|array If successful array, otherwise void. | |
| 212 | + */ | |
| 213 | + public function send_request( $url, $request, $data = [] ) { | |
| 214 | + // Attempt to get the access token. | |
| 215 | + $token = $this->get_access_token(); | |
| 216 | + if ( empty( $token ) ) { | |
| 217 | + return; | |
| 218 | + } | |
| 315 | 219 | |
| 316 | - // Update the option. | |
| 317 | - $this->update_blog_option( $this->blog_id, $key, $value ); | |
| 318 | - } | |
| 220 | + // Send the remote request using the WordPress built-in method. | |
| 221 | + $response = wp_remote_request( | |
| 222 | + $this->plugin->api_url . $url, | |
| 223 | + [ | |
| 224 | + 'method' => $request, | |
| 225 | + 'timeout' => 60, | |
| 226 | + 'redirection' => 5, | |
| 227 | + 'httpversion' => '1.0', | |
| 228 | + 'blocking' => true, | |
| 229 | + 'headers' => [ | |
| 230 | + 'Authorization' => 'Bearer ' . $token, | |
| 231 | + 'LicenseID' => $this->get_blog_option( $this->blog_id, 'patchstack_clientid', 0 ), | |
| 232 | + 'Source-Host' => get_site_url(), | |
| 233 | + ], | |
| 234 | + 'body' => $data, | |
| 235 | + 'cookies' => [], | |
| 236 | + ] | |
| 237 | + ); | |
| 238 | + | |
| 239 | + // Check error or status code. | |
| 240 | + if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) { | |
| 241 | + $this->update_blog_option( $this->blog_id, 'patchstack_api_token', '' ); | |
| 242 | + return; | |
| 319 | 243 | } |
| 320 | 244 | |
| 321 | - return $response; | |
| 245 | + return json_decode( wp_remote_retrieve_body( $response ), true ); | |
| 322 | 246 | } |
| 323 | 247 | |
| 324 | 248 | /** |
| 325 | 249 | * Send a request to our API for the IP address header. |
| 326 | - * | |
| 327 | - * @return void | |
| 328 | 250 | */ |
| 329 | 251 | public function send_header_request() |
| 330 | 252 | { |
| 331 | 253 | $header = get_option( 'patchstack_firewall_ip_header', '' ); |
| 332 | 254 | $computed = get_option( 'patchstack_ip_header_computed', 0 ); |
| 333 | - $force = get_option( 'patchstack_ip_header_force_compute', 0 ); | |
| 334 | 255 | |
| 335 | - if ( ( $header == '' && ! $computed ) || $force ) { | |
| 256 | + if ( $header == '' && ! $computed ) { | |
| 336 | 257 | // Create an OTT token. |
| 337 | 258 | $ott = md5( wp_generate_password( 32, true, true ) ); |
| 338 | 259 | update_option( 'patchstack_ott_action', $ott ); |
| 339 | 260 | |
| @@ -378,8 +299,17 @@ | ||
| 378 | 299 | return $this->send_request( '/api/rules', 'POST', $settings ); |
| 379 | 300 | } |
| 380 | 301 | |
| 381 | 302 | /** |
| 303 | + * Get the .htaccess firewall rules. | |
| 304 | + * | |
| 305 | + * @return array The .htaccess rules. | |
| 306 | + */ | |
| 307 | + public function post_firewall_htaccess_rule() { | |
| 308 | + return $this->send_request( '/api/rules/htaccess', 'POST' ); | |
| 309 | + } | |
| 310 | + | |
| 311 | + /** | |
| 382 | 312 | * Send the firewall logs to the API. |
| 383 | 313 | * |
| 384 | 314 | * @param array $logs |
| 385 | 315 | * @return array |
| @@ -448,46 +378,6 @@ | ||
| 448 | 378 | * @return void |
| 449 | 379 | */ |
| 450 | 380 | public function ping() { |
| 451 | 381 | $this->send_request( '/api/ping', 'POST', [ 'firewall' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ? 1 : 0 ] ); |
| 452 | - } | |
| 453 | - | |
| 454 | - /** | |
| 455 | - * Generate a secret value and send it to the Patchstack API for quick activation. | |
| 456 | - * | |
| 457 | - * @param string $secret | |
| 458 | - * @return void | |
| 459 | - */ | |
| 460 | - public function send_secret_token( $secret ) { | |
| 461 | - $response = wp_remote_request( | |
| 462 | - $this->plugin->api_url . '/api/secret', | |
| 463 | - [ | |
| 464 | - 'method' => 'POST', | |
| 465 | - 'timeout' => 60, | |
| 466 | - 'redirection' => 5, | |
| 467 | - 'httpversion' => '1.0', | |
| 468 | - 'blocking' => true, | |
| 469 | - 'headers' => [ | |
| 470 | - 'Source-Host' => get_site_url(), | |
| 471 | - ], | |
| 472 | - 'body' => [ | |
| 473 | - 'secret' => $secret, | |
| 474 | - 'url' => get_site_url() | |
| 475 | - ], | |
| 476 | - 'cookies' => [], | |
| 477 | - ] | |
| 478 | - ); | |
| 479 | - | |
| 480 | - // Check error or status code. | |
| 481 | - if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) != 200 ) { | |
| 482 | - return false; | |
| 483 | - } | |
| 484 | - | |
| 485 | - // Determine if auto-activation succeeded. | |
| 486 | - $result = json_decode( wp_remote_retrieve_body( $response ), true ); | |
| 487 | - if ($result && isset($result['activated'])) { | |
| 488 | - return $result['activated']; | |
| 489 | - } | |
| 490 | - | |
| 491 | - return false; | |
| 492 | 382 | } |
| 493 | 383 | } |