Changeset 3747553
- Timestamp:
- 10/15/2026 03:36:44 AM (3 weeks ago)
- Location:
- rightblogger
- Files:
-
- 6 edited
- 1 copied
-
tags/1.6.3 (copied) (copied from rightblogger/trunk )
-
tags/1.6.3/readme.txt (modified) (2 diffs)
-
tags/1.6.3/rightblogger.php (modified) (7 diffs)
-
tags/1.6.3/uninstall.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/rightblogger.php (modified) (7 diffs)
-
trunk/uninstall.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
rightblogger/tags/1.6.3/readme.txt
r3740227 r3747553 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.6. 27 Stable tag: 1.6.3 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 114 114 115 115 == Changelog == 116 117 = 1.6.3 = 118 * Fixed: Connecting no longer fails with "Security check failed" and leaves the site unconnected when WordPress asks you to log in again on the way back from RightBlogger. 116 119 117 120 = 1.6.2 = -
rightblogger/tags/1.6.3/rightblogger.php
r3739703 r3747553 4 4 * Plugin URI: https://rightblogger.com/wordpress 5 5 * Description: Connect your WordPress site to RightBlogger to publish AI-generated content directly. 6 * Version: 1.6. 26 * Version: 1.6.3 7 7 * Author: RightBlogger 8 8 * Author URI: https://rightblogger.com … … 20 20 21 21 // Plugin constants 22 define('RIGHTBLOGGER_VERSION', '1.6. 2');22 define('RIGHTBLOGGER_VERSION', '1.6.3'); 23 23 define('RIGHTBLOGGER_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('RIGHTBLOGGER_PLUGIN_URL', plugin_dir_url(__FILE__)); … … 67 67 const OPTION_BLOCK_ENABLED = 'rightblogger_studio_block_enabled'; 68 68 69 // Per-user (per-site on multisite) token that rides on the connect 70 // return_url beside the nonce. See handle_callback() for why. 71 const USER_OPTION_CONNECT_STATE = 'rightblogger_connect_state'; 72 69 73 /** 70 74 * Instance 71 75 */ 72 76 private static $instance = null; 73 77 74 78 /** 75 79 * Get instance … … 323 327 // that key unlocks the publish endpoints, which save unfiltered HTML. 324 328 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : ''; 325 if (!wp_verify_nonce($nonce, 'rightblogger_callback')) { 329 $state = isset($_GET['rb_state']) ? sanitize_text_field(wp_unslash($_GET['rb_state'])) : ''; 330 // The nonce is bound to the login session that clicked Connect, so a 331 // site that makes the admin log in again on the way back (cookie 332 // hardening, an expired session) fails it on a genuine connection and 333 // RightBlogger is left holding a key the site never saved. The state 334 // token is bound to the user instead and survives a re-login. Either 335 // proves this admin started the flow; a forged link carries neither. 336 if (!wp_verify_nonce($nonce, 'rightblogger_callback') && !$this->connect_state_matches($state)) { 326 337 add_settings_error( 327 338 'rightblogger_messages', … … 332 343 return; 333 344 } 334 345 // One use: a return_url left in history can't connect the site later. 346 delete_user_option(get_current_user_id(), self::USER_OPTION_CONNECT_STATE); 347 335 348 $status = sanitize_text_field(wp_unslash($_GET['status'])); 336 349 … … 565 578 // Format: rbwp_64hexchars 566 579 return (bool) preg_match('/^rbwp_[a-f0-9]{64}$/', $key); 580 } 581 582 /** 583 * The current user's connect state token, minted on demand. Reused while it 584 * has an hour left, so two open settings tabs agree and a click is never 585 * minutes from expiry, then rotated. handle_callback() deletes it on use. 586 */ 587 private function connect_state_token() { 588 $user_id = get_current_user_id(); 589 $state = get_user_option(self::USER_OPTION_CONNECT_STATE, $user_id); 590 if (!is_array($state) || empty($state['token']) || empty($state['expires']) 591 || (int) $state['expires'] - time() < HOUR_IN_SECONDS) { 592 $state = array( 593 'token' => wp_generate_password(32, false), 594 'expires' => time() + DAY_IN_SECONDS, 595 ); 596 update_user_option($user_id, self::USER_OPTION_CONNECT_STATE, $state); 597 } 598 return $state['token']; 599 } 600 601 /** 602 * Whether $token is the current user's unexpired connect state token. 603 */ 604 private function connect_state_matches($token) { 605 if (!is_string($token) || $token === '') { 606 return false; 607 } 608 $state = get_user_option(self::USER_OPTION_CONNECT_STATE, get_current_user_id()); 609 return is_array($state) && !empty($state['token']) && !empty($state['expires']) 610 && (int) $state['expires'] > time() 611 && hash_equals((string) $state['token'], $token); 567 612 } 568 613 … … 1140 1185 1141 1186 // Build connect URL. RightBlogger appends the connection params to 1142 // return_url and keeps its existing query, so the nonce comes back with 1143 // them and handle_callback() can tell this admin started the flow. 1187 // return_url and keeps its existing query, so the nonce and state token 1188 // come back with them and handle_callback() can tell this admin started 1189 // the flow. 1144 1190 $return_url = add_query_arg( 1145 '_wpnonce', 1146 wp_create_nonce('rightblogger_callback'), 1191 array( 1192 '_wpnonce' => wp_create_nonce('rightblogger_callback'), 1193 'rb_state' => $this->connect_state_token(), 1194 ), 1147 1195 admin_url('options-general.php?page=rightblogger') 1148 1196 ); -
rightblogger/tags/1.6.3/uninstall.php
r3739703 r3747553 29 29 delete_transient('rightblogger_conn_status'); 30 30 31 // Connect state tokens are user options, stored per site under the blog prefix. 32 global $wpdb; 33 delete_metadata('user', 0, $wpdb->get_blog_prefix() . 'rightblogger_connect_state', '', true); 34 -
rightblogger/trunk/readme.txt
r3740227 r3747553 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.6. 27 Stable tag: 1.6.3 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 114 114 115 115 == Changelog == 116 117 = 1.6.3 = 118 * Fixed: Connecting no longer fails with "Security check failed" and leaves the site unconnected when WordPress asks you to log in again on the way back from RightBlogger. 116 119 117 120 = 1.6.2 = -
rightblogger/trunk/rightblogger.php
r3739703 r3747553 4 4 * Plugin URI: https://rightblogger.com/wordpress 5 5 * Description: Connect your WordPress site to RightBlogger to publish AI-generated content directly. 6 * Version: 1.6. 26 * Version: 1.6.3 7 7 * Author: RightBlogger 8 8 * Author URI: https://rightblogger.com … … 20 20 21 21 // Plugin constants 22 define('RIGHTBLOGGER_VERSION', '1.6. 2');22 define('RIGHTBLOGGER_VERSION', '1.6.3'); 23 23 define('RIGHTBLOGGER_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('RIGHTBLOGGER_PLUGIN_URL', plugin_dir_url(__FILE__)); … … 67 67 const OPTION_BLOCK_ENABLED = 'rightblogger_studio_block_enabled'; 68 68 69 // Per-user (per-site on multisite) token that rides on the connect 70 // return_url beside the nonce. See handle_callback() for why. 71 const USER_OPTION_CONNECT_STATE = 'rightblogger_connect_state'; 72 69 73 /** 70 74 * Instance 71 75 */ 72 76 private static $instance = null; 73 77 74 78 /** 75 79 * Get instance … … 323 327 // that key unlocks the publish endpoints, which save unfiltered HTML. 324 328 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : ''; 325 if (!wp_verify_nonce($nonce, 'rightblogger_callback')) { 329 $state = isset($_GET['rb_state']) ? sanitize_text_field(wp_unslash($_GET['rb_state'])) : ''; 330 // The nonce is bound to the login session that clicked Connect, so a 331 // site that makes the admin log in again on the way back (cookie 332 // hardening, an expired session) fails it on a genuine connection and 333 // RightBlogger is left holding a key the site never saved. The state 334 // token is bound to the user instead and survives a re-login. Either 335 // proves this admin started the flow; a forged link carries neither. 336 if (!wp_verify_nonce($nonce, 'rightblogger_callback') && !$this->connect_state_matches($state)) { 326 337 add_settings_error( 327 338 'rightblogger_messages', … … 332 343 return; 333 344 } 334 345 // One use: a return_url left in history can't connect the site later. 346 delete_user_option(get_current_user_id(), self::USER_OPTION_CONNECT_STATE); 347 335 348 $status = sanitize_text_field(wp_unslash($_GET['status'])); 336 349 … … 565 578 // Format: rbwp_64hexchars 566 579 return (bool) preg_match('/^rbwp_[a-f0-9]{64}$/', $key); 580 } 581 582 /** 583 * The current user's connect state token, minted on demand. Reused while it 584 * has an hour left, so two open settings tabs agree and a click is never 585 * minutes from expiry, then rotated. handle_callback() deletes it on use. 586 */ 587 private function connect_state_token() { 588 $user_id = get_current_user_id(); 589 $state = get_user_option(self::USER_OPTION_CONNECT_STATE, $user_id); 590 if (!is_array($state) || empty($state['token']) || empty($state['expires']) 591 || (int) $state['expires'] - time() < HOUR_IN_SECONDS) { 592 $state = array( 593 'token' => wp_generate_password(32, false), 594 'expires' => time() + DAY_IN_SECONDS, 595 ); 596 update_user_option($user_id, self::USER_OPTION_CONNECT_STATE, $state); 597 } 598 return $state['token']; 599 } 600 601 /** 602 * Whether $token is the current user's unexpired connect state token. 603 */ 604 private function connect_state_matches($token) { 605 if (!is_string($token) || $token === '') { 606 return false; 607 } 608 $state = get_user_option(self::USER_OPTION_CONNECT_STATE, get_current_user_id()); 609 return is_array($state) && !empty($state['token']) && !empty($state['expires']) 610 && (int) $state['expires'] > time() 611 && hash_equals((string) $state['token'], $token); 567 612 } 568 613 … … 1140 1185 1141 1186 // Build connect URL. RightBlogger appends the connection params to 1142 // return_url and keeps its existing query, so the nonce comes back with 1143 // them and handle_callback() can tell this admin started the flow. 1187 // return_url and keeps its existing query, so the nonce and state token 1188 // come back with them and handle_callback() can tell this admin started 1189 // the flow. 1144 1190 $return_url = add_query_arg( 1145 '_wpnonce', 1146 wp_create_nonce('rightblogger_callback'), 1191 array( 1192 '_wpnonce' => wp_create_nonce('rightblogger_callback'), 1193 'rb_state' => $this->connect_state_token(), 1194 ), 1147 1195 admin_url('options-general.php?page=rightblogger') 1148 1196 ); -
rightblogger/trunk/uninstall.php
r3739703 r3747553 29 29 delete_transient('rightblogger_conn_status'); 30 30 31 // Connect state tokens are user options, stored per site under the blog prefix. 32 global $wpdb; 33 delete_metadata('user', 0, $wpdb->get_blog_prefix() . 'rightblogger_connect_state', '', true); 34
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)