-
Notifications
You must be signed in to change notification settings - Fork 822
Connection: protected owner class #43601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+779
−0
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
fa64f42
Protected owner class
bindlegirl f6a2f92
Hook into user creation
bindlegirl dae7e8a
phan and renamed email param
bindlegirl ff5363d
Add tests
bindlegirl 397552e
Minor error string change
bindlegirl 2eb32f9
Update test
bindlegirl 09a4e1c
Try fixing tests
bindlegirl dd6289d
Slimming the class
bindlegirl e02f21d
Update tests
bindlegirl a0ecb00
Everything goes to error_handler
bindlegirl 2d2cac7
Set user id to0
bindlegirl e91d3eb
Unset invalid_connection_owner on WoA
bindlegirl 8000b3a
Remove all errors
bindlegirl 6e6d8a4
Update tests
bindlegirl 9c3a65f
Update tests vol 2
bindlegirl a24762e
Adding support for pre-populating emails
bindlegirl 09bc440
Fix test
bindlegirl 86d76ad
Prepopulate role
bindlegirl 2f0aa11
Fix populating logic to depend on url
bindlegirl bb74fae
Simplify
bindlegirl ee213d8
Improve messaging
bindlegirl 0585965
Fix test
bindlegirl a7d3868
Disable chackbox
bindlegirl 5d3923d
Remove invitations
bindlegirl 5cfd8bd
jquery to vanilla js
bindlegirl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/wpcomsh/changelog/add-protected_owner_error_wpcomsh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: added | ||
|
||
Adding support for protected connection owner. |
275 changes: 275 additions & 0 deletions
275
projects/plugins/wpcomsh/connection/class-protected-owner-error-handler.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
<?php | ||
/** | ||
* The Jetpack Connection Protected Owner Error Handler class file. | ||
* | ||
* @package wpcomsh | ||
*/ | ||
|
||
namespace Automattic\WPComSH\Connection; | ||
|
||
/** | ||
* The Jetpack Connection Protected Owner Error Handler class. | ||
* | ||
* This class handles errors related to protected owner accounts in the Jetpack Connection. | ||
* It retrieves owner account errors stored in WordPress options and displays them in the UI. | ||
* | ||
* The class automatically clears errors when the required local account is created, | ||
* allowing external healing code to establish the proper Jetpack connection. | ||
* | ||
* Additionally, this class provides email prepopulation functionality for the WordPress | ||
* user creation form when creating missing protected owner accounts. It overrides the | ||
* default User_Admin class behavior to ensure the WP.com invitation checkbox is not | ||
* pre-checked when creating protected owner accounts. | ||
* | ||
* @since $$next-version$$ | ||
*/ | ||
class Protected_Owner_Error_Handler { | ||
|
||
/** | ||
* The name of the option that stores the error | ||
* | ||
* @var string | ||
*/ | ||
const STORED_ERRORS_OPTION = 'jetpack_connection_protected_owner_error'; | ||
|
||
/** | ||
* Holds the instance of this singleton class | ||
* | ||
* @var Protected_Owner_Error_Handler $instance | ||
*/ | ||
private static $instance = null; | ||
|
||
/** | ||
* Initialize instance and register hooks | ||
*/ | ||
private function __construct() { | ||
// Inject protected owner errors into the connection error system | ||
add_filter( 'jetpack_connection_get_verified_errors', array( $this, 'handle_error' ) ); | ||
|
||
// Clear errors when the missing user is created or updated (allows external healing code to work) | ||
add_action( 'user_register', array( $this, 'check_and_clear_error_for_user' ) ); | ||
add_action( 'profile_update', array( $this, 'check_and_clear_error_for_user' ) ); | ||
|
||
// Add form prepopulation functionality | ||
add_action( 'user_new_form', array( $this, 'prepopulate_user_form' ) ); | ||
|
||
// Disable WordPress.com invitations when creating protected owner accounts | ||
add_filter( 'jetpack_sso_invite_new_users_wpcom', array( $this, 'disable_wpcom_invite_for_protected_owner' ) ); | ||
} | ||
|
||
/** | ||
* Gets the instance of this singleton class | ||
* | ||
* @return Protected_Owner_Error_Handler $instance | ||
*/ | ||
public static function get_instance() { | ||
if ( self::$instance === null ) { | ||
self::$instance = new self(); | ||
} | ||
return self::$instance; | ||
} | ||
|
||
/** | ||
* Check if there's an active protected owner error | ||
* | ||
* @return array|false Raw error data if there's an active error, false otherwise. | ||
*/ | ||
private function get_active_error() { | ||
// Check if option is populated | ||
$raw_error = get_option( self::STORED_ERRORS_OPTION, false ); | ||
|
||
// Return early if no error is stored | ||
if ( ! $raw_error || ! is_array( $raw_error ) ) { | ||
return false; | ||
} | ||
|
||
// Validate the minimal required fields | ||
if ( ! isset( $raw_error['error_type'] ) || ! isset( $raw_error['email'] ) ) { | ||
return false; | ||
} | ||
|
||
// Check if user exists with the required email | ||
$user = get_user_by( 'email', $raw_error['email'] ); | ||
if ( $user ) { | ||
// User exists, delete the option and return false (no active error) | ||
$this->delete_error(); | ||
return false; | ||
} | ||
|
||
// User doesn't exist, we have an active error | ||
return $raw_error; | ||
} | ||
|
||
/** | ||
* Handle protected owner errors in the connection error system | ||
* | ||
* @param array $verified_errors Current verified errors. | ||
* @return array Updated verified errors including protected owner errors. | ||
*/ | ||
public function handle_error( $verified_errors ) { | ||
// Clear all existing errors first | ||
$verified_errors = array(); | ||
|
||
$raw_error = $this->get_active_error(); | ||
|
||
// Return early if no active error | ||
if ( ! $raw_error ) { | ||
return $verified_errors; | ||
} | ||
|
||
// Use a consistent error code for all protected owner errors | ||
$error_code = 'protected_owner_missing'; | ||
|
||
// Prepare error data for the connection error system | ||
$user_id = '0'; | ||
$timestamp = $raw_error['timestamp'] ?? time(); | ||
|
||
$error_details = array( | ||
'error_code' => $error_code, | ||
'user_id' => $user_id, | ||
'error_message' => $this->get_error_message( $raw_error['email'] ), | ||
'error_data' => array( | ||
'email' => $raw_error['email'], | ||
'error_type' => $raw_error['error_type'], | ||
'action' => 'create_missing_account', | ||
'support_url' => admin_url( 'user-new.php' ), | ||
), | ||
'timestamp' => $timestamp, | ||
'nonce' => wp_generate_password( 10, false ), | ||
'error_type' => 'protected_owner', | ||
); | ||
|
||
// Return only the protected owner error - it takes priority over other connection errors | ||
// since it's typically the root cause and other errors may be symptoms | ||
return array( | ||
$error_code => array( | ||
$user_id => $error_details, | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* Get a user-friendly error message for protected owner errors | ||
* | ||
* @param string $email The WordPress.com email address of the protected owner. | ||
* @return string The error message. | ||
*/ | ||
private function get_error_message( $email ) { | ||
return sprintf( | ||
/* translators: %s is the WordPress.com email address */ | ||
__( 'This site needs to be connected to WordPress.com by the plan owner account with email %s. Please create a local user account with this email address to resolve this issue.', 'wpcomsh' ), | ||
esc_html( $email ) | ||
); | ||
} | ||
|
||
/** | ||
* Delete the stored error | ||
*/ | ||
public function delete_error() { | ||
delete_option( self::STORED_ERRORS_OPTION ); | ||
} | ||
|
||
/** | ||
* Check if the user matches the protected owner error and clear it if so | ||
* This allows external healing code to automatically establish the connection | ||
* | ||
* @param int $user_id The ID of the user to check. | ||
*/ | ||
public function check_and_clear_error_for_user( $user_id ) { | ||
// Get the raw error data to check the email | ||
$raw_error = get_option( self::STORED_ERRORS_OPTION, false ); | ||
|
||
// Return early if no error is stored | ||
if ( ! $raw_error || ! is_array( $raw_error ) || ! isset( $raw_error['email'] ) ) { | ||
return; | ||
} | ||
|
||
// Get the user | ||
$user = get_user_by( 'id', $user_id ); | ||
if ( ! $user ) { | ||
return; | ||
} | ||
|
||
// Check if the user's email matches the required email | ||
if ( strtolower( $user->user_email ) === strtolower( $raw_error['email'] ) ) { | ||
// The user with the required email has been created/updated | ||
// Clear the error so external healing code can establish the connection | ||
$this->delete_error(); | ||
} | ||
} | ||
|
||
/** | ||
* Add form prepopulation functionality | ||
*/ | ||
public function prepopulate_user_form() { | ||
$email = $this->get_prepopulation_email(); | ||
|
||
if ( ! $email ) { | ||
return; | ||
} | ||
|
||
// Output hidden field and JavaScript to prepopulate the form | ||
?> | ||
<input type="hidden" id="jetpack_prepopulate_email" value="<?php echo esc_attr( $email ); ?>" /> | ||
<input type="hidden" name="jetpack_create_missing_account" value="1" /> | ||
|
||
<script type="text/javascript"> | ||
(function() { | ||
document.addEventListener('DOMContentLoaded', function() { | ||
// Prepopulate the email field and role | ||
var emailInput = document.getElementById('jetpack_prepopulate_email'); | ||
if (emailInput && emailInput.value) { | ||
var emailField = document.getElementById('email'); | ||
var roleField = document.getElementById('role'); | ||
|
||
if (emailField) { | ||
emailField.value = emailInput.value; | ||
} | ||
if (roleField) { | ||
roleField.value = 'administrator'; | ||
} | ||
} | ||
}); | ||
})(); | ||
</script> | ||
<?php | ||
} | ||
|
||
/** | ||
* Get the email address for prepopulation from various sources | ||
* | ||
* @return string|false Email address if available, false otherwise | ||
*/ | ||
private function get_prepopulation_email() { | ||
// Check URL parameters first (from React component) | ||
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- URL parameters are read-only for prepopulation, no sensitive actions performed | ||
if ( isset( $_GET['jetpack_protected_owner_email'] ) && | ||
isset( $_GET['jetpack_create_missing_account'] ) ) { | ||
$email = sanitize_email( wp_unslash( $_GET['jetpack_protected_owner_email'] ) ); | ||
if ( is_email( $email ) ) { | ||
return $email; | ||
} | ||
} | ||
// phpcs:enable WordPress.Security.NonceVerification.Recommended | ||
|
||
// Only prepopulate when explicitly triggered from dashboard | ||
return false; | ||
} | ||
|
||
/** | ||
* Disable WordPress.com invitations when creating protected owner accounts | ||
* | ||
* @param bool $invite_new_users_wpcom Whether to invite new users to WordPress.com. | ||
* @return bool Updated value indicating whether to invite new users to WordPress.com. | ||
*/ | ||
public function disable_wpcom_invite_for_protected_owner( $invite_new_users_wpcom ) { | ||
// Check if we're in a protected owner creation context | ||
$email = $this->get_prepopulation_email(); | ||
if ( ! $email ) { | ||
return $invite_new_users_wpcom; // Not a protected owner creation, let the default behavior proceed | ||
} | ||
|
||
// Disable invitations for protected owner creation | ||
return false; | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
projects/plugins/wpcomsh/connection/protected-owner-handlers.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
/** | ||
* Protected Owner error handling initialization for wpcomsh. | ||
* | ||
* This file loads and initializes the Protected Owner error handling | ||
* that integrates with the Jetpack connection error system. | ||
* | ||
* @package wpcomsh | ||
*/ | ||
|
||
// Require the Protected Owner Error Handler class | ||
require_once __DIR__ . '/class-protected-owner-error-handler.php'; | ||
|
||
// Initialize the Protected Owner error handler | ||
add_action( | ||
'plugins_loaded', | ||
function () { | ||
// Initialize the Protected Owner Error Handler singleton | ||
\Automattic\WPComSH\Connection\Protected_Owner_Error_Handler::get_instance(); | ||
}, | ||
5 | ||
); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.