diff --git a/projects/packages/connection/changelog/update-sso-use-package b/projects/packages/connection/changelog/update-sso-use-package new file mode 100644 index 0000000000000..e815d4c6a8a9e --- /dev/null +++ b/projects/packages/connection/changelog/update-sso-use-package @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +SSO: do not rely on the Jetpack class anymore. diff --git a/projects/packages/connection/src/class-package-version.php b/projects/packages/connection/src/class-package-version.php index 1fdc900af446a..942d27d9f23d1 100644 --- a/projects/packages/connection/src/class-package-version.php +++ b/projects/packages/connection/src/class-package-version.php @@ -12,7 +12,7 @@ */ class Package_Version { - const PACKAGE_VERSION = '2.8.0'; + const PACKAGE_VERSION = '2.8.1-alpha'; const PACKAGE_SLUG = 'connection'; diff --git a/projects/packages/connection/src/class-utils.php b/projects/packages/connection/src/class-utils.php index 1e8260c64bd8a..9abce653a1f8c 100644 --- a/projects/packages/connection/src/class-utils.php +++ b/projects/packages/connection/src/class-utils.php @@ -83,4 +83,53 @@ public static function filter_register_request_body( $properties ) { ) ); } + + /** + * Generate a new user from a SSO attempt. + * + * @param object $user_data WordPress.com user information. + */ + public static function generate_user( $user_data ) { + $username = $user_data->login; + /** + * Determines how many times the SSO module can attempt to randomly generate a user. + * + * @module sso + * + * @since jetpack-4.3.2 + * + * @param int 5 By default, SSO will attempt to random generate a user up to 5 times. + */ + $num_tries = (int) apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 ); + + $exists = username_exists( $username ); + $tries = 0; + while ( $exists && $tries++ < $num_tries ) { + $username = $user_data->login . '_' . $user_data->ID . '_' . wp_rand(); + $exists = username_exists( $username ); + } + + if ( $exists ) { + return false; + } + + $user = (object) array(); + $user->user_pass = wp_generate_password( 20 ); + $user->user_login = wp_slash( $username ); + $user->user_email = wp_slash( $user_data->email ); + $user->display_name = $user_data->display_name; + $user->first_name = $user_data->first_name; + $user->last_name = $user_data->last_name; + $user->url = $user_data->url; + $user->description = $user_data->description; + + if ( isset( $user_data->role ) && $user_data->role ) { + $user->role = $user_data->role; + } + + $created_user_id = wp_insert_user( $user ); + + update_user_meta( $created_user_id, 'wpcom_user_id', $user_data->ID ); + return get_userdata( $created_user_id ); + } } diff --git a/projects/packages/connection/src/sso/class-force-2fa.php b/projects/packages/connection/src/sso/class-force-2fa.php index 61fb4ffcc7dc9..3c037790bc1ce 100644 --- a/projects/packages/connection/src/sso/class-force-2fa.php +++ b/projects/packages/connection/src/sso/class-force-2fa.php @@ -50,10 +50,7 @@ public function plugins_loaded() { $this->role = apply_filters( 'jetpack_force_2fa_cap', 'manage_options' ); // Bail if Jetpack SSO is not active - if ( - ! class_exists( 'Jetpack' ) - || ! ( new Modules() )->is_active( 'sso' ) - ) { + if ( ! ( new Modules() )->is_active( 'sso' ) ) { add_action( 'admin_notices', array( $this, 'admin_notice' ) ); return; } @@ -75,7 +72,12 @@ public function admin_notice() { * @module SSO */ if ( apply_filters( 'jetpack_force_2fa_dependency_notice', true ) && current_user_can( $this->role ) ) { - printf( '

%2$s

', 'notice notice-warning', 'Jetpack Force 2FA requires Jetpack and the Jetpack SSO module.' ); + wp_admin_notice( + esc_html__( 'Jetpack Force 2FA requires Jetpackā€™s SSO feature.', 'jetpack-connection' ), + array( + 'type' => 'warning', + ) + ); } } diff --git a/projects/packages/connection/src/sso/class-helpers.php b/projects/packages/connection/src/sso/class-helpers.php index 200e16e83d04e..2fdf7369e4585 100644 --- a/projects/packages/connection/src/sso/class-helpers.php +++ b/projects/packages/connection/src/sso/class-helpers.php @@ -214,55 +214,6 @@ public static function allowed_redirect_hosts( $hosts, $api_base = '' ) { return array_unique( $hosts ); } - /** - * Generate a new user from a SSO attempt. - * - * @param object $user_data WordPress.com user information. - */ - public static function generate_user( $user_data ) { - $username = $user_data->login; - /** - * Determines how many times the SSO module can attempt to randomly generate a user. - * - * @module sso - * - * @since jetpack-4.3.2 - * - * @param int 5 By default, SSO will attempt to random generate a user up to 5 times. - */ - $num_tries = (int) apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 ); - - $exists = username_exists( $username ); - $tries = 0; - while ( $exists && $tries++ < $num_tries ) { - $username = $user_data->login . '_' . $user_data->ID . '_' . wp_rand(); - $exists = username_exists( $username ); - } - - if ( $exists ) { - return false; - } - - $user = (object) array(); - $user->user_pass = wp_generate_password( 20 ); - $user->user_login = wp_slash( $username ); - $user->user_email = wp_slash( $user_data->email ); - $user->display_name = $user_data->display_name; - $user->first_name = $user_data->first_name; - $user->last_name = $user_data->last_name; - $user->url = $user_data->url; - $user->description = $user_data->description; - - if ( isset( $user_data->role ) && $user_data->role ) { - $user->role = $user_data->role; - } - - $created_user_id = wp_insert_user( $user ); - - update_user_meta( $created_user_id, 'wpcom_user_id', $user_data->ID ); - return get_userdata( $created_user_id ); - } - /** * Determines how long the auth cookie is valid for when a user logs in with SSO. * diff --git a/projects/packages/connection/src/sso/class-sso.php b/projects/packages/connection/src/sso/class-sso.php index 83f63dcb6d209..b1e3bfc43c259 100644 --- a/projects/packages/connection/src/sso/class-sso.php +++ b/projects/packages/connection/src/sso/class-sso.php @@ -47,14 +47,6 @@ private function __construct() { self::$instance = $this; - /* - * This feature currently relies on the Jetpack plugin. - * Bail if Jetpack isn't installed. - */ - if ( ! class_exists( 'Jetpack' ) ) { - return; - } - add_action( 'admin_init', array( $this, 'maybe_authorize_user_after_sso' ), 1 ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'login_init', array( $this, 'login_init' ) ); @@ -71,6 +63,9 @@ private function __construct() { add_filter( 'wp_login_errors', array( $this, 'sso_reminder_logout_wpcom' ) ); + // Synchronize SSO options with WordPress.com. + add_filter( 'jetpack_sync_callable_whitelist', array( $this, 'sync_sso_callables' ), 10, 1 ); + /** * Filter to include Force 2FA feature. * @@ -134,6 +129,27 @@ public static function get_instance() { return self::$instance; } + /** + * Add SSO callables to the sync whitelist. + * + * @since $$next-version$$ + * + * @param array $callables list of callables. + * + * @return array list of callables. + */ + public function sync_sso_callables( $callables ) { + $sso_callables = array( + 'sso_is_two_step_required' => array( Helpers::class, 'is_two_step_required' ), + 'sso_should_hide_login_form' => array( Helpers::class, 'should_hide_login_form' ), + 'sso_match_by_email' => array( Helpers::class, 'match_by_email' ), + 'sso_new_user_override' => array( Helpers::class, 'new_user_override' ), + 'sso_bypass_default_login_form' => array( Helpers::class, 'bypass_login_forward_wpcom' ), + ); + + return array_merge( $callables, $sso_callables ); + } + /** * Safety heads-up added to the logout messages when SSO is enabled. * Some folks on a shared computer don't know that they need to log out of WordPress.com as well. @@ -864,7 +880,7 @@ public function handle_login() { $user_data->role = $new_user_override_role; } - $user = Helpers::generate_user( $user_data ); + $user = Utils::generate_user( $user_data ); if ( ! $user ) { $tracking->record_user_event( 'sso_login_failed', diff --git a/projects/packages/connection/tests/php/sso/test_Helpers.php b/projects/packages/connection/tests/php/sso/test_Helpers.php index ba395505d673c..ce1fa721c7873 100644 --- a/projects/packages/connection/tests/php/sso/test_Helpers.php +++ b/projects/packages/connection/tests/php/sso/test_Helpers.php @@ -9,6 +9,7 @@ namespace Automattic\Jetpack\Connection\SSO; +use Automattic\Jetpack\Connection\Utils; use Automattic\Jetpack\Constants; use WorDBless\BaseTestCase; @@ -259,7 +260,7 @@ public function test_allowed_redirect_hosts_api_base_added_on_dev_version() { * Test "generate_user_returns_user_when_username_not_exists". */ public function test_generate_user_returns_user_when_username_not_exists() { - $user = Helpers::generate_user( $this->user_data ); + $user = Utils::generate_user( $this->user_data ); $this->assertIsObject( $user ); $this->assertInstanceOf( 'WP_User', $user ); @@ -273,7 +274,7 @@ public function test_generate_user_returns_user_if_username_exists_and_has_tries add_filter( 'jetpack_sso_allowed_username_generate_retries', array( $this, 'return_one' ) ); wp_insert_user( $this->user_data ); - $user = Helpers::generate_user( $this->user_data ); + $user = Utils::generate_user( $this->user_data ); $this->assertIsObject( $user ); $this->assertInstanceOf( 'WP_User', $user ); @@ -289,7 +290,7 @@ public function test_generate_user_returns_user_if_username_exists_and_has_tries */ public function test_generate_user_sets_user_role_when_provided() { $this->user_data->role = 'administrator'; - $user = Helpers::generate_user( $this->user_data ); + $user = Utils::generate_user( $this->user_data ); $this->assertContains( 'administrator', get_userdata( $user->ID )->roles ); } diff --git a/projects/plugins/jetpack/.phan/baseline.php b/projects/plugins/jetpack/.phan/baseline.php index dd1796dbe1de1..bd3608964bb96 100644 --- a/projects/plugins/jetpack/.phan/baseline.php +++ b/projects/plugins/jetpack/.phan/baseline.php @@ -65,7 +65,6 @@ // PhanUndeclaredVariableDim : 6 occurrences // PhanImpossibleCondition : 5 occurrences // PhanNonClassMethodCall : 5 occurrences - // PhanPluginUnreachableCode : 5 occurrences // PhanTypeMismatchDimAssignment : 5 occurrences // PhanTypeSuspiciousStringExpression : 5 occurrences // PhanUndeclaredStaticProperty : 5 occurrences @@ -79,6 +78,7 @@ // PhanTypeInvalidRightOperandOfNumericOp : 4 occurrences // PhanDeprecatedFunctionInternal : 3 occurrences // PhanDeprecatedTrait : 3 occurrences + // PhanPluginUnreachableCode : 3 occurrences // PhanStaticPropIsStaticType : 3 occurrences // PhanTypeConversionFromArray : 3 occurrences // PhanTypeMismatchArgumentReal : 3 occurrences @@ -459,10 +459,6 @@ 'modules/sitemaps/sitemap-librarian.php' => ['PhanTypeMismatchArgument', 'PhanTypeMismatchReturnProbablyReal'], 'modules/sitemaps/sitemap-logger.php' => ['PhanTypeMismatchProperty'], 'modules/sitemaps/sitemaps.php' => ['PhanNoopNew', 'PhanTypeMismatchArgument'], - 'modules/sso.php' => ['PhanNoopNew', 'PhanRedundantCondition', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturn', 'PhanTypeMismatchReturnProbablyReal'], - 'modules/sso/class-jetpack-force-2fa.php' => ['PhanDeprecatedFunction'], - 'modules/sso/class.jetpack-sso-helpers.php' => ['PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturn'], - 'modules/sso/class.jetpack-sso-user-admin.php' => ['PhanPluginUnreachableCode', 'PhanTypeArraySuspiciousNullable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal'], 'modules/stats.php' => ['PhanDeprecatedFunction', 'PhanPossiblyUndeclaredVariable', 'PhanRedundantCondition', 'PhanSuspiciousMagicConstant', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturn', 'PhanTypeMismatchReturnNullable', 'PhanTypeMismatchReturnProbablyReal', 'PhanTypeMissingReturn'], 'modules/subscriptions.php' => ['PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchDefault', 'PhanTypeMismatchReturnProbablyReal', 'PhanTypeSuspiciousNonTraversableForeach'], 'modules/subscriptions/subscribe-modal/class-jetpack-subscribe-modal.php' => ['PhanTypeMismatchReturnNullable'], diff --git a/projects/plugins/jetpack/changelog/update-sso-use-package b/projects/plugins/jetpack/changelog/update-sso-use-package new file mode 100644 index 0000000000000..603c1003038eb --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-sso-use-package @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +SSO: switch to loading feature from the Connection package. diff --git a/projects/plugins/jetpack/class.jetpack.php b/projects/plugins/jetpack/class.jetpack.php index 5a9c6653c4ba8..f81a31592dcf2 100644 --- a/projects/plugins/jetpack/class.jetpack.php +++ b/projects/plugins/jetpack/class.jetpack.php @@ -1079,28 +1079,13 @@ public function point_edit_comment_links_to_calypso( $url ) { * @return array list of callables. */ public function filter_sync_callable_whitelist( $callables ) { - // Jetpack Functions. $jetpack_callables = array( 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ), 'updates' => array( 'Jetpack', 'get_updates' ), 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins. ); - $callables = array_merge( $callables, $jetpack_callables ); - - // Jetpack_SSO_Helpers. - if ( include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) { - $sso_helpers = array( - 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ), - 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ), - 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ), - 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ), - 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ), - ); - $callables = array_merge( $callables, $sso_helpers ); - } - - return $callables; + return array_merge( $callables, $jetpack_callables ); } /** diff --git a/projects/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php b/projects/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php index 27a885bb9f59a..769caaa47b307 100644 --- a/projects/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php @@ -1,5 +1,6 @@ user_data['email'] ); $roles = (array) $this->user_data['roles']; @@ -76,7 +76,7 @@ public function create_or_get_user() { $this->user_data->url = isset( $this->user_data->URL ) ? $this->user_data->URL : ''; $this->user_data->display_name = $this->user_data->name; $this->user_data->description = ''; - $user = Jetpack_SSO_Helpers::generate_user( $this->user_data ); + $user = Utils::generate_user( $this->user_data ); } if ( is_multisite() ) { diff --git a/projects/plugins/jetpack/modules/sso.php b/projects/plugins/jetpack/modules/sso.php index d2f9fe9087bc2..57e6fe44b41bf 100644 --- a/projects/plugins/jetpack/modules/sso.php +++ b/projects/plugins/jetpack/modules/sso.php @@ -1,20 +1,4 @@ is_wpcom_platform() ) { - return $errors; - } - - if ( ! empty( $errors->errors['loggedout'] ) ) { - $logout_message = wp_kses( - sprintf( - /* translators: %1$s is a link to the WordPress.com account settings page. */ - __( 'If you are on a shared computer, remember to also log out of WordPress.com.', 'jetpack' ), - 'https://wordpress.com/me' - ), - array( - 'a' => array( - 'href' => array(), - ), - ) - ); - $errors->add( 'jetpack-sso-show-logout', $logout_message, 'message' ); - } - return $errors; - } - - /** - * If jetpack_force_logout == 1 in current user meta the user will be forced - * to logout and reauthenticate with the site. - **/ - public function maybe_logout_user() { - global $current_user; - - if ( 1 === (int) $current_user->jetpack_force_logout ) { - delete_user_meta( $current_user->ID, 'jetpack_force_logout' ); - Jetpack_SSO_Helpers::delete_connection_for_user( $current_user->ID ); - wp_logout(); - wp_safe_redirect( wp_login_url() ); - exit; - } - } - - /** - * Adds additional methods the WordPress xmlrpc API for handling SSO specific features - * - * @param array $methods API methods. - * @return array - **/ - public function xmlrpc_methods( $methods ) { - $methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' ); - return $methods; - } - - /** - * Marks a user's profile for disconnect from WordPress.com and forces a logout - * the next time the user visits the site. - * - * @param int $user_id User to disconnect from the site. - **/ - public function xmlrpc_user_disconnect( $user_id ) { - $user_query = new WP_User_Query( - array( - 'meta_key' => 'wpcom_user_id', - 'meta_value' => $user_id, - ) - ); - $user = $user_query->get_results(); - $user = $user[0]; - - if ( $user instanceof WP_User ) { - $user = wp_set_current_user( $user->ID ); - update_user_meta( $user->ID, 'jetpack_force_logout', '1' ); - Jetpack_SSO_Helpers::delete_connection_for_user( $user->ID ); - return true; - } - return false; - } - - /** - * Enqueues scripts and styles necessary for SSO login. - */ - public function login_enqueue_scripts() { - global $action; - - if ( ! Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) { - return; - } - - if ( is_rtl() ) { - wp_enqueue_style( - 'jetpack-sso-login', - plugins_url( 'modules/sso/jetpack-sso-login-rtl.css', JETPACK__PLUGIN_FILE ), - array( 'login', 'genericons' ), - JETPACK__VERSION - ); - } else { - wp_enqueue_style( - 'jetpack-sso-login', - plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), - array( 'login', 'genericons' ), - JETPACK__VERSION - ); - } - - wp_enqueue_script( - 'jetpack-sso-login', - plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), - array(), - JETPACK__VERSION, - false - ); - } - - /** - * Adds Jetpack SSO classes to login body - * - * @param array $classes Array of classes to add to body tag. - * @return array Array of classes to add to body tag. - */ - public function login_body_class( $classes ) { - global $action; - - if ( ! Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) { - return $classes; - } - - // Always add the jetpack-sso class so that we can add SSO specific styling even when the SSO form isn't being displayed. - $classes[] = 'jetpack-sso'; - - if ( ! ( new Status() )->is_staging_site() ) { - /** - * Should we show the SSO login form? - * - * $_GET['jetpack-sso-default-form'] is used to provide a fallback in case JavaScript is not enabled. - * - * The default_to_sso_login() method allows us to dynamically decide whether we show the SSO login form or not. - * The SSO module uses the method to display the default login form if we can not find a user to log in via SSO. - * But, the method could be filtered by a site admin to always show the default login form if that is preferred. - */ - if ( empty( $_GET['jetpack-sso-show-default-form'] ) && Jetpack_SSO_Helpers::show_sso_login() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $classes[] = 'jetpack-sso-form-display'; - } - } - - return $classes; - } - - /** - * Inlined admin styles for SSO. - */ - public function print_inline_admin_css() { - ?> - - General > Secure Sign On that allows users to - * turn off the login form on wp-login.php - * - * @since 2.7 - **/ - public function register_settings() { - - add_settings_section( - 'jetpack_sso_settings', - __( 'Secure Sign On', 'jetpack' ), - '__return_false', - 'jetpack-sso' - ); - - /* - * Settings > General > Secure Sign On - * Require two step authentication - */ - register_setting( - 'jetpack-sso', - 'jetpack_sso_require_two_step', - array( $this, 'validate_jetpack_sso_require_two_step' ) - ); - - add_settings_field( - 'jetpack_sso_require_two_step', - '', // Output done in render $callback: __( 'Require Two-Step Authentication' , 'jetpack' ). - array( $this, 'render_require_two_step' ), - 'jetpack-sso', - 'jetpack_sso_settings' - ); - - /* - * Settings > General > Secure Sign On - */ - register_setting( - 'jetpack-sso', - 'jetpack_sso_match_by_email', - array( $this, 'validate_jetpack_sso_match_by_email' ) - ); - - add_settings_field( - 'jetpack_sso_match_by_email', - '', // Output done in render $callback: __( 'Match by Email' , 'jetpack' ). - array( $this, 'render_match_by_email' ), - 'jetpack-sso', - 'jetpack_sso_settings' - ); - } - - /** - * Builds the display for the checkbox allowing user to require two step - * auth be enabled on WordPress.com accounts before login. Displays in Settings > General - * - * @since 2.7 - **/ - public function render_require_two_step() { - ?> - - General. - * - * @param bool $input The jetpack_sso_require_two_step option setting. - * - * @since 2.7 - * @return boolean - **/ - public function validate_jetpack_sso_require_two_step( $input ) { - return ( ! empty( $input ) ) ? 1 : 0; - } - - /** - * Builds the display for the checkbox allowing the user to allow matching logins by email - * Displays in Settings > General - * - * @since 2.9 - **/ - public function render_match_by_email() { - ?> - - General. - * - * @param bool $input The jetpack_sso_match_by_email option setting. - * - * @since 2.9 - * @return boolean - **/ - public function validate_jetpack_sso_match_by_email( $input ) { - return ( ! empty( $input ) ) ? 1 : 0; - } - - /** - * Checks to determine if the user wants to login on wp-login - * - * This function mostly exists to cover the exceptions to login - * that may exist as other parameters to $_GET[action] as $_GET[action] - * does not have to exist. By default WordPress assumes login if an action - * is not set, however this may not be true, as in the case of logout - * where $_GET[loggedout] is instead set - * - * @return boolean - **/ - private function wants_to_login() { - $wants_to_login = false; - - // Cover default WordPress behavior. - $action = isset( $_REQUEST['action'] ) ? filter_var( wp_unslash( $_REQUEST['action'] ) ) : 'login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - - // And now the exceptions. - $action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - - if ( Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) { - $wants_to_login = true; - } - - return $wants_to_login; - } - - /** - * Checks to determine if the user has indicated they want to use the wp-admin interface. - */ - private function use_wp_admin_interface() { - return 'wp-admin' === get_option( 'wpcom_admin_interface' ); - } - - /** - * Initialization for a SSO request. - */ - public function login_init() { - global $action; - - $tracking = new Tracking(); - - if ( Jetpack_SSO_Helpers::should_hide_login_form() ) { - /** - * Since the default authenticate filters fire at priority 20 for checking username and password, - * let's fire at priority 30. wp_authenticate_spam_check is fired at priority 99, but since we return a - * WP_Error in disable_default_login_form, then we won't trigger spam processing logic. - */ - add_filter( 'authenticate', array( 'Jetpack_SSO_Notices', 'disable_default_login_form' ), 30 ); - - /** - * Filter the display of the disclaimer message appearing when default WordPress login form is disabled. - * - * @module sso - * - * @since 2.8.0 - * - * @param bool true Should the disclaimer be displayed. Default to true. - */ - $display_sso_disclaimer = apply_filters( 'jetpack_sso_display_disclaimer', true ); - if ( $display_sso_disclaimer ) { - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'msg_login_by_jetpack' ) ); - } - } - - if ( 'jetpack-sso' === $action ) { - if ( isset( $_GET['result'] ) && isset( $_GET['user_id'] ) && isset( $_GET['sso_nonce'] ) && 'success' === $_GET['result'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $this->handle_login(); - $this->display_sso_login_form(); - } elseif ( ( new Status() )->is_staging_site() ) { - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'sso_not_allowed_in_staging' ) ); - } else { - // Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect? - add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) ); - $reauth = ! empty( $_GET['force_reauth'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $sso_url = $this->get_sso_url_or_die( $reauth ); - - $tracking->record_user_event( 'sso_login_redirect_success' ); - wp_safe_redirect( $sso_url ); - exit; - } - } elseif ( Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) { - - // Save cookies so we can handle redirects after SSO. - static::save_cookies(); - - /** - * Check to see if the site admin wants to automagically forward the user - * to the WordPress.com login page AND that the request to wp-login.php - * is not something other than login (Like logout!) - */ - if ( ! $this->use_wp_admin_interface() && Jetpack_SSO_Helpers::bypass_login_forward_wpcom() && $this->wants_to_login() ) { - add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) ); - $reauth = ! empty( $_GET['force_reauth'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $sso_url = $this->get_sso_url_or_die( $reauth ); - $tracking->record_user_event( 'sso_login_redirect_bypass_success' ); - wp_safe_redirect( $sso_url ); - exit; - } - - $this->display_sso_login_form(); - } - } - - /** - * Ensures that we can get a nonce from WordPress.com via XML-RPC before setting - * up the hooks required to display the SSO form. - */ - public function display_sso_login_form() { - add_filter( 'login_body_class', array( $this, 'login_body_class' ) ); - add_action( 'login_head', array( $this, 'print_inline_admin_css' ) ); - - if ( ( new Status() )->is_staging_site() ) { - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'sso_not_allowed_in_staging' ) ); - return; - } - - $sso_nonce = self::request_initial_nonce(); - if ( is_wp_error( $sso_nonce ) ) { - return; - } - - add_action( 'login_form', array( $this, 'login_form' ) ); - add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) ); - } - - /** - * Conditionally save the redirect_to url as a cookie. - * - * @since 4.6.0 Renamed to save_cookies from maybe_save_redirect_cookies - */ - public static function save_cookies() { - if ( headers_sent() ) { - return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) ); - } - - setcookie( - 'jetpack_sso_original_request', - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw(). - esc_url_raw( set_url_scheme( ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) ), - time() + HOUR_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - - if ( ! empty( $_GET['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - // If we have something to redirect to. - $url = esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true ); - } elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) { - // Otherwise, if it's already set, purge it. - setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true ); - } - } - - /** - * Outputs the Jetpack SSO button and description as well as the toggle link - * for switching between Jetpack SSO and default login. - */ - public function login_form() { - $site_name = get_bloginfo( 'name' ); - if ( ! $site_name ) { - $site_name = get_bloginfo( 'url' ); - } - - $display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) - ? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) - : false; - $gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) - ? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) - : false; - - ?> -
- -
- - -

- %s', 'jetpack' ), esc_html( $display_name ) ), - array( 'span' => true ) - ); - ?> -

-
- - - - -
- build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?> - - - - - - -

- -

- -
- - -
- -
- - - - - - - - - -
- is_user_connected() ) { - Jetpack_SSO_Helpers::delete_connection_for_user( get_current_user_id() ); - } - } - - /** - * Retrieves nonce used for SSO form. - * - * @return string|WP_Error - */ - public static function request_initial_nonce() { - $nonce = ! empty( $_COOKIE['jetpack_sso_nonce'] ) - ? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) ) - : false; - - if ( ! $nonce ) { - $xml = new Jetpack_IXR_Client(); - $xml->query( 'jetpack.sso.requestNonce' ); - - if ( $xml->isError() ) { - return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() ); - } - - $nonce = sanitize_key( $xml->getResponse() ); - - setcookie( - 'jetpack_sso_nonce', - $nonce, - time() + ( 10 * MINUTE_IN_SECONDS ), - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - } - - return $nonce; - } - - /** - * The function that actually handles the login! - */ - public function handle_login() { - $wpcom_nonce = isset( $_GET['sso_nonce'] ) ? sanitize_key( $_GET['sso_nonce'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $wpcom_user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - - $xml = new Jetpack_IXR_Client(); - $xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id ); - - $user_data = $xml->isError() ? false : $xml->getResponse(); - if ( empty( $user_data ) ) { - add_filter( 'jetpack_sso_default_to_sso_login', '__return_false' ); - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_invalid_response_data' ) ); - return; - } - - $user_data = (object) $user_data; - $user = null; - - /** - * Fires before Jetpack's SSO modifies the log in form. - * - * @module sso - * - * @since 2.6.0 - * - * @param object $user_data WordPress.com User information. - */ - do_action( 'jetpack_sso_pre_handle_login', $user_data ); - - $tracking = new Tracking(); - - if ( Jetpack_SSO_Helpers::is_two_step_required() && 0 === (int) $user_data->two_step_enabled ) { - $this->user_data = $user_data; - - $tracking->record_user_event( - 'sso_login_failed', - array( - 'error_message' => 'error_msg_enable_two_step', - ) - ); - - $error = new WP_Error( 'two_step_required', __( 'You must have Two-Step Authentication enabled on your WordPress.com account.', 'jetpack' ) ); - - /** This filter is documented in core/src/wp-includes/pluggable.php */ - do_action( 'wp_login_failed', $user_data->login, $error ); - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_msg_enable_two_step' ) ); - return; - } - - $user_found_with = ''; - if ( empty( $user ) && isset( $user_data->external_user_id ) ) { - $user_found_with = 'external_user_id'; - $user = get_user_by( 'id', (int) $user_data->external_user_id ); - if ( $user ) { - $expected_id = get_user_meta( $user->ID, 'wpcom_user_id', true ); - if ( $expected_id && $expected_id != $user_data->ID ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison, Universal.Operators.StrictComparisons.LooseNotEqual - $error = new WP_Error( 'expected_wpcom_user', __( 'Something got a little mixed up and an unexpected WordPress.com user logged in.', 'jetpack' ) ); - - $tracking->record_user_event( - 'sso_login_failed', - array( - 'error_message' => 'error_unexpected_wpcom_user', - ) - ); - - /** This filter is documented in core/src/wp-includes/pluggable.php */ - do_action( 'wp_login_failed', $user_data->login, $error ); - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_invalid_response_data' ) ); // @todo Need to have a better notice. This is only for the sake of testing the validation. - return; - } - update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID ); - } - } - - // If we don't have one by wpcom_user_id, try by the email? - if ( empty( $user ) && Jetpack_SSO_Helpers::match_by_email() ) { - $user_found_with = 'match_by_email'; - $user = get_user_by( 'email', $user_data->email ); - if ( $user ) { - update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID ); - } - } - - // If we've still got nothing, create the user. - $new_user_override_role = Jetpack_SSO_Helpers::new_user_override( $user_data ); - if ( empty( $user ) && ( get_option( 'users_can_register' ) || $new_user_override_role ) ) { - /** - * If not matching by email we still need to verify the email does not exist - * or this blows up - * - * If match_by_email is true, we know the email doesn't exist, as it would have - * been found in the first pass. If get_user_by( 'email' ) doesn't find the - * user, then we know that email is unused, so it's safe to add. - */ - if ( Jetpack_SSO_Helpers::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) { - - if ( $new_user_override_role ) { - $user_data->role = $new_user_override_role; - } - - $user = Jetpack_SSO_Helpers::generate_user( $user_data ); - if ( ! $user ) { - $tracking->record_user_event( - 'sso_login_failed', - array( - 'error_message' => 'could_not_create_username', - ) - ); - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_unable_to_create_user' ) ); - return; - } - - $user_found_with = $new_user_override_role - ? 'user_created_new_user_override' - : 'user_created_users_can_register'; - } else { - $tracking->record_user_event( - 'sso_login_failed', - array( - 'error_message' => 'error_msg_email_already_exists', - ) - ); - - $this->user_data = $user_data; - add_action( 'login_message', array( 'Jetpack_SSO_Notices', 'error_msg_email_already_exists' ) ); - return; - } - } - - /** - * Fires after we got login information from WordPress.com. - * - * @module sso - * - * @since 2.6.0 - * - * @param WP_User|false|null $user Local User information. - * @param object $user_data WordPress.com User Login information. - */ - do_action( 'jetpack_sso_handle_login', $user, $user_data ); - - if ( $user ) { - // Cache the user's details, so we can present it back to them on their user screen. - update_user_meta( $user->ID, 'wpcom_user_data', $user_data ); - - add_filter( 'auth_cookie_expiration', array( 'Jetpack_SSO_Helpers', 'extend_auth_cookie_expiration_for_sso' ) ); - wp_set_auth_cookie( $user->ID, true ); - remove_filter( 'auth_cookie_expiration', array( 'Jetpack_SSO_Helpers', 'extend_auth_cookie_expiration_for_sso' ) ); - - /** This filter is documented in core/src/wp-includes/user.php */ - do_action( 'wp_login', $user->user_login, $user ); - - wp_set_current_user( $user->ID ); - - $_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url(); - - // If we have a saved redirect to request in a cookie. - if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) { - // Set that as the requested redirect to. - $redirect_to = esc_url_raw( wp_unslash( $_COOKIE['jetpack_sso_redirect_to'] ) ); - $_request_redirect_to = $redirect_to; - } - - $json_api_auth_environment = Jetpack_SSO_Helpers::get_json_api_auth_environment(); - - $is_json_api_auth = ! empty( $json_api_auth_environment ); - $is_user_connected = Jetpack_SSO_Helpers::is_user_connected( $user->ID ); - $roles = new Roles(); - $tracking->record_user_event( - 'sso_user_logged_in', - array( - 'user_found_with' => $user_found_with, - 'user_connected' => (bool) $is_user_connected, - 'user_role' => $roles->translate_current_user_to_role(), - 'is_json_api_auth' => (bool) $is_json_api_auth, - ) - ); - - if ( $is_json_api_auth ) { - $authorize_json_api = new Authorize_Json_Api(); - $authorize_json_api->verify_json_api_authorization_request( $json_api_auth_environment ); - $authorize_json_api->store_json_api_authorization_token( $user->user_login, $user ); - - } elseif ( ! $is_user_connected ) { - wp_safe_redirect( - add_query_arg( - array( - 'redirect_to' => $redirect_to, - 'request_redirect_to' => $_request_redirect_to, - 'calypso_env' => ( new Host() )->get_calypso_env(), - 'jetpack-sso-auth-redirect' => '1', - ), - admin_url() - ) - ); - exit; - } - - add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) ); - wp_safe_redirect( - /** This filter is documented in core/src/wp-login.php */ - apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user ) - ); - exit; - } - - add_filter( 'jetpack_sso_default_to_sso_login', '__return_false' ); - - $tracking->record_user_event( - 'sso_login_failed', - array( - 'error_message' => 'cant_find_user', - ) - ); - - $this->user_data = $user_data; - - $error = new WP_Error( 'account_not_found', __( 'Account not found. If you already have an account, make sure you have connected to WordPress.com.', 'jetpack' ) ); - - /** This filter is documented in core/src/wp-includes/pluggable.php */ - do_action( 'wp_login_failed', $user_data->login, $error ); - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'cant_find_user' ) ); - } - - /** - * Retrieve the admin profile page URL. - */ - public static function profile_page_url() { - return admin_url( 'profile.php' ); - } - - /** - * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page. - * - * @param array $args An array of arguments to add to the SSO URL. - * @param boolean $is_primary If the button have the `button-primary` class. - * @return string Returns the HTML markup for the button. - */ - public function build_sso_button( $args = array(), $is_primary = false ) { - $url = $this->build_sso_button_url( $args ); - $classes = $is_primary - ? 'jetpack-sso button button-primary' - : 'jetpack-sso button'; - - return sprintf( - '%3$s %4$s', - esc_url( $url ), - $classes, - '', - esc_html__( 'Log in with WordPress.com', 'jetpack' ) - ); - } - - /** - * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO. - * - * @param array $args An array of arguments to add to the SSO URL. - * @return string The URL used for SSO. - */ - public function build_sso_button_url( $args = array() ) { - $defaults = array( - 'action' => 'jetpack-sso', - ); - - $args = wp_parse_args( $args, $defaults ); - - if ( ! empty( $_GET['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $args['redirect_to'] = rawurlencode( esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - } - - return add_query_arg( $args, wp_login_url() ); - } - - /** - * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies. - * - * @param boolean $reauth If the user be forced to reauthenticate on WordPress.com. - * @param array $args Optional query parameters. - * @return string The WordPress.com SSO URL. - */ - public function get_sso_url_or_die( $reauth = false, $args = array() ) { - $custom_login_url = Jetpack_SSO_Helpers::get_custom_login_url(); - if ( $custom_login_url ) { - $args['login_url'] = rawurlencode( $custom_login_url ); - } - - if ( empty( $reauth ) ) { - $sso_redirect = $this->build_sso_url( $args ); - } else { - Jetpack_SSO_Helpers::clear_wpcom_profile_cookies(); - $sso_redirect = $this->build_reauth_and_sso_url( $args ); - } - - // If there was an error retrieving the SSO URL, then error. - if ( is_wp_error( $sso_redirect ) ) { - $error_message = sanitize_text_field( - sprintf( '%s: %s', $sso_redirect->get_error_code(), $sso_redirect->get_error_message() ) - ); - $tracking = new Tracking(); - $tracking->record_user_event( - 'sso_login_redirect_failed', - array( - 'error_message' => $error_message, - ) - ); - wp_die( esc_html( $error_message ) ); - } - - return $sso_redirect; - } - - /** - * Build WordPress.com SSO URL with appropriate query parameters. - * - * @param array $args Optional query parameters. - * @return string|WP_Error WordPress.com SSO URL - */ - public function build_sso_url( $args = array() ) { - $sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce(); - $defaults = array( - 'action' => 'jetpack-sso', - 'site_id' => Jetpack_Options::get_option( 'id' ), - 'sso_nonce' => $sso_nonce, - 'calypso_auth' => '1', - ); - - $args = wp_parse_args( $args, $defaults ); - - if ( is_wp_error( $sso_nonce ) ) { - return $sso_nonce; - } - - return add_query_arg( $args, 'https://wordpress.com/wp-login.php' ); - } - - /** - * Build WordPress.com SSO URL with appropriate query parameters, - * including the parameters necessary to force the user to reauthenticate - * on WordPress.com. - * - * @param array $args Optional query parameters. - * @return string|WP_Error WordPress.com SSO URL - */ - public function build_reauth_and_sso_url( $args = array() ) { - $sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce(); - $redirect = $this->build_sso_url( - array( - 'force_auth' => '1', - 'sso_nonce' => $sso_nonce, - ) - ); - - if ( is_wp_error( $redirect ) ) { - return $redirect; - } - - $defaults = array( - 'action' => 'jetpack-sso', - 'site_id' => Jetpack_Options::get_option( 'id' ), - 'sso_nonce' => $sso_nonce, - 'reauth' => '1', - 'redirect_to' => rawurlencode( $redirect ), - 'calypso_auth' => '1', - ); - - $args = wp_parse_args( $args, $defaults ); - - if ( is_wp_error( $args['sso_nonce'] ) ) { - return $args['sso_nonce']; - } - - return add_query_arg( $args, 'https://wordpress.com/wp-login.php' ); - } - - /** - * Determines local user associated with a given WordPress.com user ID. - * - * @since 2.6.0 - * - * @param int $wpcom_user_id User ID from WordPress.com. - * @return object Local user object if found, null if not. - */ - public static function get_user_by_wpcom_id( $wpcom_user_id ) { - $user_query = new WP_User_Query( - array( - 'meta_key' => 'wpcom_user_id', - 'meta_value' => (int) $wpcom_user_id, - 'number' => 1, - ) - ); - - $users = $user_query->get_results(); - return $users ? array_shift( $users ) : null; - } - - /** - * When jetpack-sso-auth-redirect query parameter is set, will redirect user to - * WordPress.com authorization flow. - * - * We redirect here instead of in handle_login() because Jetpack::init()->build_connect_url - * calls menu_page_url() which doesn't work properly until admin menus are registered. - */ - public function maybe_authorize_user_after_sso() { - if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - return; - } - - $redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : admin_url(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['request_redirect_to'] ) ) : $redirect_to; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - - /** This filter is documented in core/src/wp-login.php */ - $redirect_after_auth = apply_filters( 'login_redirect', $redirect_to, $request_redirect_to, wp_get_current_user() ); - - /** - * Since we are passing this redirect to WordPress.com and therefore can not use wp_safe_redirect(), - * let's sanitize it here to make sure it's safe. If the redirect is not safe, then use admin_url(). - */ - $redirect_after_auth = wp_sanitize_redirect( $redirect_after_auth ); - $redirect_after_auth = wp_validate_redirect( $redirect_after_auth, admin_url() ); - - /** - * Return the raw connect URL with our redirect and attribute connection to SSO. - * We remove any other filters that may be turning on the in-place connection - * since we will be redirecting the user as opposed to iFraming. - */ - remove_all_filters( 'jetpack_use_iframe_authorization_flow' ); - add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false' ); - $connect_url = Jetpack::init()->build_connect_url( true, $redirect_after_auth, 'sso' ); - - add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) ); - wp_safe_redirect( $connect_url ); - exit; - } - - /** - * Cache user's display name and Gravatar so it can be displayed on the login screen. These cookies are - * stored when the user logs out, and then deleted when the user logs in. - */ - public function store_wpcom_profile_cookies_on_logout() { - $user_id = get_current_user_id(); - - if ( ! Jetpack_SSO_Helpers::is_user_connected( $user_id ) ) { - return; - } - - $user_data = $this->get_user_data( $user_id ); - if ( ! $user_data ) { - return; - } - - setcookie( - 'jetpack_sso_wpcom_name_' . COOKIEHASH, - $user_data->display_name, - time() + WEEK_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - - setcookie( - 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH, - get_avatar_url( - $user_data->email, - array( - 'size' => 144, - 'default' => 'mystery', - ) - ), - time() + WEEK_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - } - - /** - * Determines if a local user is connected to WordPress.com - * - * @since 2.8 - * @param integer $user_id - Local user id. - * @return boolean - **/ - public function is_user_connected( $user_id ) { - return $this->get_user_data( $user_id ); - } - - /** - * Retrieves a user's WordPress.com data - * - * @since 2.8 - * @param integer $user_id - Local user id. - * @return mixed null or stdClass - **/ - public function get_user_data( $user_id ) { - return get_user_meta( $user_id, 'wpcom_user_data', true ); - } -} - -Jetpack_SSO::get_instance(); +/** + * Legacy, deprecated class. + * + * @deprecated $$next-version$$ + */ +class Jetpack_SSO extends SSO {} diff --git a/projects/plugins/jetpack/modules/sso/class-jetpack-force-2fa.php b/projects/plugins/jetpack/modules/sso/class-jetpack-force-2fa.php index 8dc845a9632c9..36bf05372b851 100644 --- a/projects/plugins/jetpack/modules/sso/class-jetpack-force-2fa.php +++ b/projects/plugins/jetpack/modules/sso/class-jetpack-force-2fa.php @@ -4,11 +4,17 @@ * * Ported from original repo at https://github.com/automattic/jetpack-force-2fa * + * @deprecated $$next-version$$ Use Automattic\Jetpack\Connection\Manager\SSO instead. + * + * phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + * * @package automattic/jetpack */ /** * Force users to use two factor authentication. + * + * @deprecated $$next-version$$ */ class Jetpack_Force_2FA { @@ -18,157 +24,47 @@ class Jetpack_Force_2FA { * Defaults to manage_options via the plugins_loaded function. * Can be modified with the jetpack_force_2fa_cap filter. * + * @deprecated $$next-version$$ + * * @var string */ private $role; /** * Constructor. + * + * @deprecated $$next-version$$ */ public function __construct() { - add_action( 'after_setup_theme', array( $this, 'plugins_loaded' ) ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Force_2FA::__construct' ); } /** * Load the plugin via the plugins_loaded hook. + * + * @deprecated $$next-version$$ */ public function plugins_loaded() { - /** - * Filter the role to force 2FA for. - * Defaults to manage_options. - * - * @param string $role The role to force 2FA for. - * @return string - * @since 12.7 - * @module SSO - */ - $this->role = apply_filters( 'jetpack_force_2fa_cap', 'manage_options' ); - - // Bail if Jetpack SSO is not active - if ( ! class_exists( 'Jetpack' ) || ! Jetpack::is_active() || ! Jetpack::is_module_active( 'sso' ) ) { - add_action( 'admin_notices', array( $this, 'admin_notice' ) ); - return; - } - - $this->force_2fa(); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Force_2FA::plugins_loaded' ); } /** * Display an admin notice if Jetpack SSO is not active. - */ - public function admin_notice() { - /** - * Filter if an admin notice is deplayed when Force 2FA is required, but SSO is not enabled. - * Defaults to true. - * - * @param bool $display_notice Whether to display the notice. - * @return bool - * @since 12.7 - * @module SSO - */ - if ( apply_filters( 'jetpack_force_2fa_dependency_notice', true ) && current_user_can( $this->role ) ) { - printf( '

%2$s

', 'notice notice-warning', 'Jetpack Force 2FA requires Jetpack and the Jetpack SSO module.' ); - } - } - - /** - * Force 2FA when using Jetpack SSO and force Jetpack SSO. * - * @return void + * @deprecated $$next-version$$ */ - private function force_2fa() { - // Allows WP.com login to a local account if it matches the local account. - add_filter( 'jetpack_sso_match_by_email', '__return_true', 9999 ); - - // multisite - if ( is_multisite() ) { - - // Hide the login form - add_filter( 'jetpack_remove_login_form', '__return_true', 9999 ); - add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true', 9999 ); - add_filter( 'jetpack_sso_display_disclaimer', '__return_false', 9999 ); - - add_filter( - 'wp_authenticate_user', - function () { - return new WP_Error( 'wpcom-required', $this->get_login_error_message() ); }, - 9999 - ); - - add_filter( 'jetpack_sso_require_two_step', '__return_true' ); - - add_filter( 'allow_password_reset', '__return_false' ); - } else { - // Not multisite. - - // Completely disable the standard login form for admins. - add_filter( - 'wp_authenticate_user', - function ( $user ) { - if ( is_wp_error( $user ) ) { - return $user; - } - if ( $user->has_cap( $this->role ) ) { - return new WP_Error( 'wpcom-required', $this->get_login_error_message(), $user->user_login ); - } - return $user; - }, - 9999 - ); - - add_filter( - 'allow_password_reset', - function ( $allow, $user_id ) { - if ( user_can( $user_id, $this->role ) ) { - return false; - } - return $allow; }, - 9999, - 2 - ); - - add_action( 'jetpack_sso_pre_handle_login', array( $this, 'jetpack_set_two_step' ) ); - } + public function admin_notice() { + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Force_2FA::admin_notice' ); } /** * Specifically set the two step filter for Jetpack SSO. * - * @param Object $user_data The user data from WordPress.com. + * @deprecated $$next-version$$ * - * @return void + * @param Object $user_data The user data from WordPress.com. */ public function jetpack_set_two_step( $user_data ) { - $user = Jetpack_SSO::get_user_by_wpcom_id( $user_data->ID ); - - // Borrowed from Jetpack. Ignores the match_by_email setting. - if ( empty( $user ) ) { - $user = get_user_by( 'email', $user_data->email ); - } - - if ( $user && $user->has_cap( $this->role ) ) { - add_filter( 'jetpack_sso_require_two_step', '__return_true' ); - } - } - - /** - * Get the login error message. - * - * @return string - */ - private function get_login_error_message() { - /** - * Filter the login error message. - * Defaults to a message that explains the user must use a WordPress.com account with 2FA enabled. - * - * @param string $message The login error message. - * @return string - * @since 12.7 - * @module SSO - */ - return apply_filters( - 'jetpack_force_2fa_login_error_message', - sprintf( 'For added security, please log in using your WordPress.com account.

Note: Your account must have Two Step Authentication enabled, which can be configured from Security Settings.', 'https://support.wordpress.com/security/two-step-authentication/', 'https://wordpress.com/me/security/two-step' ) - ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Force_2FA::jetpack_set_two_step' ); } } diff --git a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-helpers.php b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-helpers.php index af544b26384d0..6f2dfd6061260 100644 --- a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-helpers.php +++ b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-helpers.php @@ -2,91 +2,65 @@ /** * A collection of helper functions used in the SSO module. * + * @deprecated $$next-version$$ Use Automattic\Jetpack\Connection\Manager\SSO instead. + * * @package automattic/jetpack */ -use Automattic\Jetpack\Connection\Manager as Connection_Manager; +use Automattic\Jetpack\Connection\Manager; +use Automattic\Jetpack\Connection\SSO\Helpers; +use Automattic\Jetpack\Connection\Utils; if ( ! class_exists( 'Jetpack_SSO_Helpers' ) ) : /** * A collection of helper functions used in the SSO module. * + * @deprecated $$next-version$$ + * * @since 4.1.0 */ class Jetpack_SSO_Helpers { /** * Determine if the login form should be hidden or not * + * @deprecated $$next-version$$ + * * @return bool **/ public static function should_hide_login_form() { - /** - * Remove the default log in form, only leave the WordPress.com log in button. - * - * @module sso - * - * @since 3.1.0 - * - * @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false. - */ - return (bool) apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::should_hide_login_form' ); + + return Helpers::should_hide_login_form(); } /** * Returns a boolean value for whether logging in by matching the WordPress.com user email to a * Jetpack site user's email is allowed. * + * @deprecated $$next-version$$ + * * @return bool */ public static function match_by_email() { - $match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : (bool) get_option( 'jetpack_sso_match_by_email', true ); - - /** - * Link the local account to an account on WordPress.com using the same email address. - * - * @module sso - * - * @since 2.6.0 - * - * @param bool $match_by_email Should we link the local account to an account on WordPress.com using the same email address. Default to false. - */ - return (bool) apply_filters( 'jetpack_sso_match_by_email', $match_by_email ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::match_by_email' ); + + return Helpers::match_by_email(); } /** * Returns a boolean for whether users are allowed to register on the Jetpack site with SSO, * even though the site disallows normal registrations. * + * @deprecated $$next-version$$ + * * @param object|null $user_data WordPress.com user information. * @return bool */ public static function new_user_override( $user_data = null ) { - $new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false; - - /** - * Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations. - * If you return a string that corresponds to a user role, the user will be given that role. - * - * @module sso - * - * @since 2.6.0 - * @since 4.6 $user_data object is now passed to the jetpack_sso_new_user_override filter - * - * @param bool $new_user_override Allow users to register on your site with a WordPress.com account. Default to false. - * @param object|null $user_data An object containing the user data returned from WordPress.com. - */ - $role = apply_filters( 'jetpack_sso_new_user_override', $new_user_override, $user_data ); - - if ( $role ) { - if ( is_string( $role ) && get_role( $role ) ) { - return $role; - } else { - return get_option( 'default_role' ); - } - } - - return false; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::new_user_override' ); + + return Helpers::new_user_override( $user_data ); } /** @@ -94,38 +68,28 @@ public static function new_user_override( $user_data = null ) { * * @since 4.1.0 * + * @deprecated $$next-version$$ + * * @return bool */ public static function is_two_step_required() { - /** - * Is it required to have 2-step authentication enabled on WordPress.com to use SSO? - * - * @module sso - * - * @since 2.8.0 - * - * @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication? - */ - return (bool) apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step', false ) ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::is_two_step_required' ); + + return Helpers::is_two_step_required(); } /** * Returns a boolean for whether a user that is attempting to log in will be automatically * redirected to WordPress.com to begin the SSO flow. * + * @deprecated $$next-version$$ + * * @return bool */ public static function bypass_login_forward_wpcom() { - /** - * Redirect the site's log in form to WordPress.com's log in form. - * - * @module sso - * - * @since 3.1.0 - * - * @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form. - */ - return (bool) apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::bypass_login_forward_wpcom' ); + + return Helpers::bypass_login_forward_wpcom(); } /** @@ -134,23 +98,14 @@ public static function bypass_login_forward_wpcom() { * * @since 4.1.0 * + * @deprecated $$next-version$$ + * * @return bool */ public static function show_sso_login() { - if ( self::should_hide_login_form() ) { - return true; - } - - /** - * Display the SSO login form as the default when both the default and SSO login forms are enabled. - * - * @module sso - * - * @since 4.1.0 - * - * @param bool true Should the SSO login form be displayed by default when the default login form is also enabled? - */ - return (bool) apply_filters( 'jetpack_sso_default_to_sso_login', true ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::show_sso_login' ); + + return Helpers::show_sso_login(); } /** @@ -158,10 +113,14 @@ public static function show_sso_login() { * * @since 4.1.0 * + * @deprecated $$next-version$$ + * * @return bool */ public static function is_require_two_step_checkbox_disabled() { - return (bool) has_filter( 'jetpack_sso_require_two_step' ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::is_require_two_step_checkbox_disabled' ); + + return Helpers::is_require_two_step_checkbox_disabled(); } /** @@ -169,10 +128,14 @@ public static function is_require_two_step_checkbox_disabled() { * * @since 4.1.0 * + * @deprecated $$next-version$$ + * * @return bool */ public static function is_match_by_email_checkbox_disabled() { - return defined( 'WPCC_MATCH_BY_EMAIL' ) || has_filter( 'jetpack_sso_match_by_email' ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::is_match_by_email_checkbox_disabled' ); + + return Helpers::is_match_by_email_checkbox_disabled(); } /** @@ -184,97 +147,43 @@ public static function is_match_by_email_checkbox_disabled() { * @since 4.3.0 * @since 4.6.0 Added public-api.wordpress.com as an allowed redirect * + * @deprecated $$next-version$$ + * * @param array $hosts Allowed redirect hosts. * @param string $api_base Base API URL. * * @return array */ public static function allowed_redirect_hosts( $hosts, $api_base = JETPACK__API_BASE ) { - if ( empty( $hosts ) ) { - $hosts = array(); - } - - $hosts[] = 'wordpress.com'; - $hosts[] = 'jetpack.wordpress.com'; - $hosts[] = 'public-api.wordpress.com'; - $hosts[] = 'jetpack.com'; - - if ( ! str_contains( $api_base, 'jetpack.wordpress.com/jetpack' ) ) { - $base_url_parts = wp_parse_url( esc_url_raw( $api_base ) ); - if ( $base_url_parts && ! empty( $base_url_parts['host'] ) ) { - $hosts[] = $base_url_parts['host']; - } - } - - return array_unique( $hosts ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::allowed_redirect_hosts' ); + + return Helpers::allowed_redirect_hosts( $hosts, $api_base ); } /** * Generate a new user from a SSO attempt. * + * @deprecated $$next-version$$ + * * @param object $user_data WordPress.com user information. */ public static function generate_user( $user_data ) { - $username = $user_data->login; - /** - * Determines how many times the SSO module can attempt to randomly generate a user. - * - * @module sso - * - * @since 4.3.2 - * - * @param int 5 By default, SSO will attempt to random generate a user up to 5 times. - */ - $num_tries = (int) apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 ); - - $exists = username_exists( $username ); - $tries = 0; - while ( $exists && $tries++ < $num_tries ) { - $username = $user_data->login . '_' . $user_data->ID . '_' . wp_rand(); - $exists = username_exists( $username ); - } - - if ( $exists ) { - return false; - } - - $user = (object) array(); - $user->user_pass = wp_generate_password( 20 ); - $user->user_login = wp_slash( $username ); - $user->user_email = wp_slash( $user_data->email ); - $user->display_name = $user_data->display_name; - $user->first_name = $user_data->first_name; - $user->last_name = $user_data->last_name; - $user->url = $user_data->url; - $user->description = $user_data->description; - - if ( isset( $user_data->role ) && $user_data->role ) { - $user->role = $user_data->role; - } - - $created_user_id = wp_insert_user( $user ); - - update_user_meta( $created_user_id, 'wpcom_user_id', $user_data->ID ); - return get_userdata( $created_user_id ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Utils::generate_user' ); + + return Utils::generate_user( $user_data ); } /** * Determines how long the auth cookie is valid for when a user logs in with SSO. * + * @deprecated $$next-version$$ + * * @return int result of the jetpack_sso_auth_cookie_expiration filter. */ public static function extend_auth_cookie_expiration_for_sso() { - /** - * Determines how long the auth cookie is valid for when a user logs in with SSO. - * - * @module sso - * - * @since 4.4.0 - * @since 6.1.0 Fixed a typo. Filter was previously jetpack_sso_auth_cookie_expirtation. - * - * @param int YEAR_IN_SECONDS - */ - return (int) apply_filters( 'jetpack_sso_auth_cookie_expiration', YEAR_IN_SECONDS ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::extend_auth_cookie_expiration_for_sso' ); + + return Helpers::extend_auth_cookie_expiration_for_sso(); } /** @@ -282,29 +191,16 @@ public static function extend_auth_cookie_expiration_for_sso() { * * @since 4.6.0 * + * @deprecated $$next-version$$ + * * @param string $action SSO action being performed. * * @return bool Is SSO allowed for the current action? */ public static function display_sso_form_for_action( $action ) { - /** - * Allows plugins the ability to overwrite actions where the SSO form is allowed to be used. - * - * @module sso - * - * @since 4.6.0 - * - * @param array $allowed_actions_for_sso - */ - $allowed_actions_for_sso = (array) apply_filters( - 'jetpack_sso_allowed_actions', - array( - 'login', - 'jetpack-sso', - 'jetpack_json_api_authorization', - ) - ); - return in_array( $action, $allowed_actions_for_sso, true ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::display_sso_form_for_action' ); + + return Helpers::display_sso_form_for_action( $action ); } /** @@ -313,120 +209,53 @@ public static function display_sso_form_for_action( $action ) { * * @since 4.6.0 * + * @deprecated $$next-version$$ + * * @return array|bool */ public static function get_json_api_auth_environment() { - if ( empty( $_COOKIE['jetpack_sso_original_request'] ) ) { - return false; - } - - $original_request = esc_url_raw( wp_unslash( $_COOKIE['jetpack_sso_original_request'] ) ); - - $parsed_url = wp_parse_url( $original_request ); - if ( empty( $parsed_url ) || empty( $parsed_url['query'] ) ) { - return false; - } - - $args = array(); - wp_parse_str( $parsed_url['query'], $args ); - - if ( empty( $args ) || empty( $args['action'] ) ) { - return false; - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::get_json_api_auth_environment' ); - if ( 'jetpack_json_api_authorization' !== $args['action'] ) { - return false; - } - - return array_merge( - $args, - array( 'jetpack_json_api_original_query' => $original_request ) - ); + return Helpers::get_json_api_auth_environment(); } /** * Check if the site has a custom login page URL, and return it. * If default login page URL is used (`wp-login.php`), `null` will be returned. * + * @deprecated $$next-version$$ + * * @return string|null */ public static function get_custom_login_url() { - $login_url = wp_login_url(); - - if ( str_ends_with( $login_url, 'wp-login.php' ) ) { - // No custom URL found. - return null; - } - - $site_url = trailingslashit( site_url() ); - - if ( ! str_starts_with( $login_url, $site_url ) ) { - // Something went wrong, we can't properly extract the custom URL. - return null; - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::get_custom_login_url' ); - // Extracting the "path" part of the URL, because we don't need the `site_url` part. - return str_ireplace( $site_url, '', $login_url ); + return Helpers::get_custom_login_url(); } /** * Clear the cookies that store the profile information for the last * WPCOM user to connect. + * + * @deprecated $$next-version$$ */ public static function clear_wpcom_profile_cookies() { - if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) { - setcookie( - 'jetpack_sso_wpcom_name_' . COOKIEHASH, - ' ', - time() - YEAR_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - } - - if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) { - setcookie( - 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH, - ' ', - time() - YEAR_IN_SECONDS, - COOKIEPATH, - COOKIE_DOMAIN, - is_ssl(), - true - ); - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::clear_wpcom_profile_cookies' ); + + return Helpers::clear_wpcom_profile_cookies(); } /** * Remove an SSO connection for a user. * + * @deprecated $$next-version$$ + * * @param int $user_id The local user id. */ public static function delete_connection_for_user( $user_id ) { - $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ); - if ( ! $wpcom_user_id ) { - return; - } - - $xml = new Jetpack_IXR_Client( - array( - 'wpcom_user_id' => $user_id, - ) - ); - $xml->query( 'jetpack.sso.removeUser', $wpcom_user_id ); - - if ( $xml->isError() ) { - return false; - } - - // Clean up local data stored for SSO. - delete_user_meta( $user_id, 'wpcom_user_id' ); - delete_user_meta( $user_id, 'wpcom_user_data' ); - self::clear_wpcom_profile_cookies(); - - return $xml->getResponse(); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\Helpers::delete_connection_for_user' ); + + return Helpers::delete_connection_for_user( $user_id ); } /** @@ -434,14 +263,14 @@ public static function delete_connection_for_user( $user_id ) { * * @since 13.3 * + * @deprecated $$next-version$$ + * * @param int $user_id Local User information. */ public static function is_user_connected( $user_id = 0 ) { - if ( ! $user_id ) { - $user_id = get_current_user_id(); - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager->is_user_connected' ); - return ( new Connection_Manager( 'jetpack' ) )->is_user_connected( $user_id ); + return ( new Manager() )->is_user_connected( $user_id ); } } diff --git a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-notices.php b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-notices.php index 57f9f9149a2d6..d027eacfcd529 100644 --- a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-notices.php +++ b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-notices.php @@ -2,16 +2,20 @@ /** * A collection of helper functions used in the SSO module. * + * @deprecated $$next-version$$ Use Automattic\Jetpack\Connection\Manager\SSO instead. + * * @package automattic/jetpack */ -use Automattic\Jetpack\Redirect; +use Automattic\Jetpack\Connection\SSO\Notices; if ( ! class_exists( 'Jetpack_SSO_Notices' ) ) : /** * A collection of helper functions used in the SSO module. * + * @deprecated $$next-version$$ + * * @since 4.4.0 */ class Jetpack_SSO_Notices { @@ -20,26 +24,16 @@ class Jetpack_SSO_Notices { * the user's account on WordPress.com does not have two step enabled. * * @since 2.7 + * + * @deprecated $$next-version$$ + * * @param string $message Error message. * @return string **/ public static function error_msg_enable_two_step( $message ) { - $error = sprintf( - wp_kses( - /* translators: URL to settings page */ - __( - 'Two-Step Authentication is required to access this site. Please visit your Security Settings to configure Two-step Authentication for your account.', - 'jetpack' - ), - array( 'a' => array( 'href' => array() ) ) - ), - Redirect::get_url( 'calypso-me-security-two-step' ), - Redirect::get_url( 'wpcom-support-security-two-step-authentication' ) - ); - - $message .= sprintf( '

%s

', $error ); - - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::error_msg_enable_two_step' ); + + return Notices::error_msg_enable_two_step( $message ); } /** @@ -47,25 +41,15 @@ public static function error_msg_enable_two_step( $message ) { * is off and they already have an account with their email address on * this site. * + * @deprecated $$next-version$$ + * * @param string $message Error message. * @return string */ public static function error_msg_email_already_exists( $message ) { - $error = sprintf( - wp_kses( - /* translators: login URL */ - __( - 'You already have an account on this site. Please sign in with your username and password and then connect to WordPress.com.', - 'jetpack' - ), - array( 'a' => array( 'href' => array() ) ) - ), - esc_url_raw( add_query_arg( 'jetpack-sso-show-default-form', '1', wp_login_url() ) ) - ); - - $message .= sprintf( '

%s

', $error ); - - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::error_msg_email_already_exists' ); + + return Notices::error_msg_email_already_exists( $message ); } /** @@ -73,14 +57,16 @@ public static function error_msg_email_already_exists( $message ) { * * @since 4.3.2 * + * @deprecated $$next-version$$ + * * @param string $message Error Message. * * @return string */ public static function error_msg_identity_crisis( $message ) { - $error = esc_html__( 'Logging in with WordPress.com is not currently available because this site is experiencing connection problems.', 'jetpack' ); - $message .= sprintf( '

%s

', $error ); - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::error_msg_identity_crisis' ); + + return Notices::error_msg_identity_crisis( $message ); } /** @@ -89,17 +75,16 @@ public static function error_msg_identity_crisis( $message ) { * * @since 4.3.2 * + * @deprecated $$next-version$$ + * * @param string $message Error message. * * @return string */ public static function error_invalid_response_data( $message ) { - $error = esc_html__( - 'There was an error logging you in via WordPress.com, please try again or try logging in with your username and password.', - 'jetpack' - ); - $message .= sprintf( '

%s

', $error ); - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::error_invalid_response_data' ); + + return Notices::error_invalid_response_data( $message ); } /** @@ -108,38 +93,32 @@ public static function error_invalid_response_data( $message ) { * * @since 4.3.2 * + * @deprecated $$next-version$$ + * * @param string $message Error message. * * @return string */ public static function error_unable_to_create_user( $message ) { - $error = esc_html__( - 'There was an error creating a user for you. Please contact the administrator of your site.', - 'jetpack' - ); - $message .= sprintf( '

%s

', $error ); - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::error_unable_to_create_user' ); + + return Notices::error_unable_to_create_user( $message ); } /** * When the default login form is hidden, this method is called on the 'authenticate' filter with a priority of 30. * This method disables the ability to submit the default login form. * + * @deprecated $$next-version$$ + * * @param WP_User|WP_Error $user Either the user attempting to login or an existing authentication failure. * * @return WP_Error */ public static function disable_default_login_form( $user ) { - if ( is_wp_error( $user ) ) { - return $user; - } - - /** - * Since we're returning an error that will be shown as a red notice, let's remove the - * informational "blue" notice. - */ - remove_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'msg_login_by_jetpack' ) ); - return new WP_Error( 'jetpack_sso_required', self::get_sso_required_message() ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::disable_default_login_form' ); + + return Notices::disable_default_login_form( $user ); } /** @@ -147,65 +126,45 @@ public static function disable_default_login_form( $user ) { * login form in Settings > General > Secure Sign On * * @since 2.7 + * + * @deprecated $$next-version$$ + * * @param string $message Error message. * * @return string **/ public static function msg_login_by_jetpack( $message ) { - $message .= sprintf( '

%s

', self::get_sso_required_message() ); - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::msg_login_by_jetpack' ); + + return Notices::msg_login_by_jetpack( $message ); } /** * Get the message for SSO required. * + * @deprecated $$next-version$$ + * * @return string */ public static function get_sso_required_message() { - $msg = esc_html__( - 'A WordPress.com account is required to access this site. Click the button below to sign in or create a free WordPress.com account.', - 'jetpack' - ); - - /** - * Filter the message displayed when the default WordPress login form is disabled. - * - * @module sso - * - * @since 2.8.0 - * - * @param string $msg Disclaimer when default WordPress login form is disabled. - */ - return apply_filters( 'jetpack_sso_disclaimer_message', $msg ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::get_sso_required_message' ); + + return Notices::get_sso_required_message(); } /** * Message displayed when the user can not be found after approving the SSO process on WordPress.com * + * @deprecated $$next-version$$ + * * @param string $message Error message. * * @return string */ public static function cant_find_user( $message ) { - $error = __( - "We couldn't find your account. If you already have an account, make sure you have connected to WordPress.com.", - 'jetpack' - ); - - /** - * Filters the "couldn't find your account" notice after an attempted SSO. - * - * @module sso - * - * @since 10.5.0 - * - * @param string $error Error text. - */ - $error = apply_filters( 'jetpack_sso_unknown_user_notice', $error ); - - $message .= sprintf( '

%s

', esc_html( $error ) ); - - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::cant_find_user' ); + + return Notices::cant_find_user( $message ); } /** @@ -213,28 +172,16 @@ public static function cant_find_user( $message ) { * * @since 4.4.0 * + * @deprecated $$next-version$$ + * * @param string $message Error message. * * @return string */ public static function sso_not_allowed_in_staging( $message ) { - $error = __( - 'Logging in with WordPress.com is disabled for sites that are in staging mode.', - 'jetpack' - ); - - /** - * Filters the disallowed notice for staging sites attempting SSO. - * - * @module sso - * - * @since 10.5.0 - * - * @param string $error Error text. - */ - $error = apply_filters( 'jetpack_sso_disallowed_staging_notice', $error ); - $message .= sprintf( '

%s

', esc_html( $error ) ); - return $message; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\Manager\\SSO\\Notices::sso_not_allowed_in_staging' ); + + return Notices::sso_not_allowed_in_staging( $message ); } } diff --git a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-user-admin.php b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-user-admin.php index f35eb626d40ee..d084eb3f553ad 100644 --- a/projects/plugins/jetpack/modules/sso/class.jetpack-sso-user-admin.php +++ b/projects/plugins/jetpack/modules/sso/class.jetpack-sso-user-admin.php @@ -1,815 +1,170 @@ 'defer', - 'in_footer' => true, - 'enqueue' => true, - ) - ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->jetpack_new_users_styles' ); } /** * Intercept the arguments for building the table, and create WP_User_Query instance * - * @param array $args The search arguments. + * @deprecated $$next-version$$ * - * @return array + * @param array $args The search arguments. */ public function set_user_query( $args ) { - self::$user_search = new WP_User_Query( $args ); - return $args; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->set_user_query' ); } /** * Revokes WordPress.com invitation. * + * @deprecated $$next-version$$ + * * @param int $user_id The user ID. */ public function revoke_user_invite( $user_id ) { - try { - $has_pending_invite = self::has_pending_wpcom_invite( $user_id ); - - if ( $has_pending_invite ) { - $response = self::send_revoke_wpcom_invite( $has_pending_invite ); - $event = 'sso_user_invite_revoked'; - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - $body = json_decode( wp_remote_retrieve_body( $response ) ); - $tracking_event_data = array( - 'success' => 'false', - 'error_code' => 'invalid-revoke-api-error', - ); - - if ( ! empty( $body ) && ! empty( $body->message ) ) { - $tracking_event_data['error_message'] = $body->message; - } - self::$tracking->record_user_event( - $event, - $tracking_event_data - ); - return $response; - } - - $body = json_decode( $response['body'] ); - - if ( ! $body->deleted ) { - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => 'invalid-invite-revoke', - ) - ); - } else { - self::$tracking->record_user_event( $event, array( 'success' => 'true' ) ); - } - - return $response; - } else { - // Delete external contributor if it exists. - $wpcom_user_data = ( new Manager() )->get_connected_user_data( $user_id ); - if ( isset( $wpcom_user_data['ID'] ) ) { - return self::delete_external_contributor( $wpcom_user_data['ID'] ); - } - } - } catch ( Exception $e ) { - return false; - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->revoke_user_invite' ); } /** * Renders invitations errors/success messages in users.php. + * + * @deprecated $$next-version$$ */ public function handle_invitation_results() { - $valid_nonce = isset( $_GET['_wpnonce'] ) - ? wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'jetpack-sso-invite-user' ) - : false; - - if ( ! $valid_nonce || ! isset( $_GET['jetpack-sso-invite-user'] ) ) { - return; - } - if ( $_GET['jetpack-sso-invite-user'] === 'success' ) { - return wp_admin_notice( __( 'User was invited successfully!', 'jetpack' ), array( 'type' => 'success' ) ); - } - if ( $_GET['jetpack-sso-invite-user'] === 'reinvited-success' ) { - return wp_admin_notice( __( 'User was re-invited successfully!', 'jetpack' ), array( 'type' => 'success' ) ); - } - - if ( $_GET['jetpack-sso-invite-user'] === 'successful-revoke' ) { - return wp_admin_notice( __( 'User invite revoked successfully.', 'jetpack' ), array( 'type' => 'success' ) ); - } - - if ( $_GET['jetpack-sso-invite-user'] === 'failed' && isset( $_GET['jetpack-sso-api-error-message'] ) ) { - return wp_admin_notice( wp_kses( wp_unslash( $_GET['jetpack-sso-api-error-message'] ), array() ), array( 'type' => 'error' ) ); - } - - if ( $_GET['jetpack-sso-invite-user'] === 'failed' && isset( $_GET['jetpack-sso-invite-error'] ) ) { - switch ( $_GET['jetpack-sso-invite-error'] ) { - case 'invalid-user': - return wp_admin_notice( __( 'Tried to invite a user that doesn’t exist.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-email': - return wp_admin_notice( __( 'Tried to invite a user that doesn’t have an email address.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-user-permissions': - return wp_admin_notice( __( 'You don’t have permission to invite users.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-user-revoke': - return wp_admin_notice( __( 'Tried to revoke an invite for a user that doesn’t exist.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-invite-revoke': - return wp_admin_notice( __( 'Tried to revoke an invite that doesn’t exist.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-revoke-permissions': - return wp_admin_notice( __( 'You don’t have permission to revoke invites.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'empty-invite': - return wp_admin_notice( __( 'There is no previous invite for this user', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-invite': - return wp_admin_notice( __( 'Attempted to send a new invitation to a user using an invite that doesn’t exist.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'error-revoke': - return wp_admin_notice( __( 'An error has occurred when revoking the invite for the user.', 'jetpack' ), array( 'type' => 'error' ) ); - case 'invalid-revoke-api-error': - return wp_admin_notice( __( 'An error has occurred when revoking the user invite.', 'jetpack' ), array( 'type' => 'error' ) ); - default: - return wp_admin_notice( __( 'An error has occurred when inviting the user to the site.', 'jetpack' ), array( 'type' => 'error' ) ); - } - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->handle_invitation_results' ); } /** * Invites a user to connect to WordPress.com to allow them to log in via SSO. + * + * @deprecated $$next-version$$ */ public function invite_user_to_wpcom() { - check_admin_referer( 'jetpack-sso-invite-user', 'invite_nonce' ); - $nonce = wp_create_nonce( 'jetpack-sso-invite-user' ); - $event = 'sso_user_invite_sent'; - - if ( ! current_user_can( 'create_users' ) ) { - $error = 'invalid-user-permissions'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - return self::create_error_notice_and_redirect( $query_params ); - } elseif ( isset( $_GET['user_id'] ) ) { - $user_id = intval( wp_unslash( $_GET['user_id'] ) ); - $user = get_user_by( 'id', $user_id ); - $user_email = $user->user_email; - - if ( ! $user || ! $user_email ) { - $reason = ! $user ? 'invalid-user' : 'invalid-email'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $reason, - '_wpnonce' => $nonce, - ); - - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $reason, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - $blog_id = Manager::get_site_id( true ); - $roles = new Roles(); - $user_role = $roles->translate_user_to_role( $user ); - - $url = '/sites/' . $blog_id . '/invites/new'; - $response = Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array( - 'method' => 'POST', - ), - array( - 'invitees' => array( - array( - 'email_or_username' => $user_email, - 'role' => $user_role, - ), - ), - ), - 'wpcom' - ); - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - $error_code = 'invalid-invite-api-error'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error_code, - '_wpnonce' => $nonce, - ); - - $tracking_event_data = array( - 'success' => 'false', - 'error_code' => $error_code, - ); - - $body = json_decode( wp_remote_retrieve_body( $response ) ); - if ( ! empty( $body ) && ! empty( $body->message ) ) { - $query_params['jetpack-sso-api-error-message'] = $body->message; - $tracking_event_data['error_message'] = $body->message; - } - - self::$tracking->record_user_event( - $event, - $tracking_event_data - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - // access the first item since we're inviting one user. - $body = json_decode( $response['body'] )[0]; - - $query_params = array( - 'jetpack-sso-invite-user' => $body->success ? 'success' : 'failed', - '_wpnonce' => $nonce, - ); - - if ( ! $body->success && $body->errors ) { - $response_error = array_keys( (array) $body->errors ); - $query_params['jetpack-sso-invite-error'] = $response_error[0]; - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $response_error[0], - ) - ); - } else { - self::$tracking->record_user_event( $event, array( 'success' => 'true' ) ); - } - - return self::create_error_notice_and_redirect( $query_params ); - } else { - $error = 'invalid-user'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - wp_die(); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->invite_user_to_wpcom' ); } /** * Revokes a user's invitation to connect to WordPress.com. * + * @deprecated $$next-version$$ + * * @param string $invite_id The ID of the invite to revoke. */ public function send_revoke_wpcom_invite( $invite_id ) { - $blog_id = Manager::get_site_id( true ); - - $url = '/sites/' . $blog_id . '/invites/delete'; - return Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array( - 'method' => 'POST', - ), - array( - 'invite_ids' => array( $invite_id ), - ), - 'wpcom' - ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->send_revoke_wpcom_invite' ); } /** * Handles logic to revoke user invite. + * + * @deprecated $$next-version$$ */ public function handle_request_revoke_invite() { - check_admin_referer( 'jetpack-sso-revoke-user-invite', 'revoke_invite_nonce' ); - $nonce = wp_create_nonce( 'jetpack-sso-invite-user' ); - $event = 'sso_user_invite_revoked'; - if ( ! current_user_can( 'promote_users' ) ) { - $error = 'invalid-revoke-permissions'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - - return self::create_error_notice_and_redirect( $query_params ); - } elseif ( isset( $_GET['user_id'] ) ) { - $user_id = intval( wp_unslash( $_GET['user_id'] ) ); - $user = get_user_by( 'id', $user_id ); - if ( ! $user ) { - $error = 'invalid-user-revoke'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - if ( ! isset( $_GET['invite_id'] ) ) { - $error = 'invalid-invite-revoke'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - $invite_id = sanitize_text_field( wp_unslash( $_GET['invite_id'] ) ); - $response = self::send_revoke_wpcom_invite( $invite_id ); - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - $error = 'invalid-revoke-api-error'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, // general error message - '_wpnonce' => $nonce, - ); - - $tracking_event_data = array( - 'success' => 'false', - 'error_code' => $error, - ); - - $body = json_decode( wp_remote_retrieve_body( $response ) ); - if ( ! empty( $body ) && ! empty( $body->message ) ) { - $query_params['jetpack-sso-api-error-message'] = $body->message; - $tracking_event_data['error_message'] = $body->message; - } - - self::$tracking->record_user_event( - $event, - $tracking_event_data - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - $body = json_decode( $response['body'] ); - $query_params = array( - 'jetpack-sso-invite-user' => $body->deleted ? 'successful-revoke' : 'failed', - '_wpnonce' => $nonce, - ); - if ( ! $body->deleted ) { // no invite was deleted, probably it does not exist - $error = 'invalid-invite-revoke'; - $query_params['jetpack-sso-invite-error'] = $error; - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - } else { - self::$tracking->record_user_event( $event, array( 'success' => 'true' ) ); - } - return self::create_error_notice_and_redirect( $query_params ); - } else { - $error = 'invalid-user-revoke'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $error, - '_wpnonce' => $nonce, - ); - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - wp_die(); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->handle_request_revoke_invite' ); } /** * Handles resend user invite. + * + * @deprecated $$next-version$$ */ public function handle_request_resend_invite() { - check_admin_referer( 'jetpack-sso-resend-user-invite', 'resend_invite_nonce' ); - $nonce = wp_create_nonce( 'jetpack-sso-invite-user' ); - $event = 'sso_user_invite_resend'; - if ( ! current_user_can( 'create_users' ) ) { - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => 'invalid-user-permissions', - '_wpnonce' => $nonce, - ); - return self::create_error_notice_and_redirect( $query_params ); - } elseif ( isset( $_GET['invite_id'] ) ) { - $invite_slug = sanitize_text_field( wp_unslash( $_GET['invite_id'] ) ); - $blog_id = Manager::get_site_id( true ); - $url = '/sites/' . $blog_id . '/invites/resend'; - $response = Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array( - 'method' => 'POST', - ), - array( - 'invite_slug' => $invite_slug, - ), - 'wpcom' - ); - - $status_code = wp_remote_retrieve_response_code( $response ); - - if ( 200 !== $status_code ) { - $message_type = $status_code === 404 ? 'invalid-invite' : ''; // empty is the general error message - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => $message_type, - '_wpnonce' => $nonce, - ); - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $message_type, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } - - $body = json_decode( $response['body'] ); - $invite_response_message = $body->success ? 'reinvited-success' : 'failed'; - $query_params = array( - 'jetpack-sso-invite-user' => $invite_response_message, - '_wpnonce' => $nonce, - ); - - if ( ! $body->success ) { - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $invite_response_message, - ) - ); - } else { - self::$tracking->record_user_event( $event, array( 'success' => 'true' ) ); - } - - return self::create_error_notice_and_redirect( $query_params ); - } else { - $error = 'empty-invite'; - $query_params = array( - 'jetpack-sso-invite-user' => 'failed', - 'jetpack-sso-invite-error' => 'empty-invite', - '_wpnonce' => $nonce, - ); - self::$tracking->record_user_event( - $event, - array( - 'success' => 'false', - 'error_message' => $error, - ) - ); - return self::create_error_notice_and_redirect( $query_params ); - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->handle_request_resend_invite' ); } /** * Adds 'Revoke invite' and 'Resend invite' link to user table row actions. * Removes 'Reset password' link. * + * @deprecated $$next-version$$ + * * @param array $actions - User row actions. * @param WP_User $user_object - User object. */ public function jetpack_user_table_row_actions( $actions, $user_object ) { - $user_id = $user_object->ID; - $has_pending_invite = self::has_pending_wpcom_invite( $user_id ); - - if ( current_user_can( 'promote_users' ) && $has_pending_invite ) { - $nonce = wp_create_nonce( 'jetpack-sso-revoke-user-invite' ); - $actions['sso_revoke_invite'] = sprintf( - '%s', - add_query_arg( - array( - 'action' => 'jetpack_revoke_invite_user_to_wpcom', - 'user_id' => $user_id, - 'revoke_invite_nonce' => $nonce, - 'invite_id' => $has_pending_invite, - ), - admin_url( 'admin-post.php' ) - ), - esc_html__( 'Revoke invite', 'jetpack' ) - ); - } - if ( current_user_can( 'promote_users' ) && $has_pending_invite ) { - $nonce = wp_create_nonce( 'jetpack-sso-resend-user-invite' ); - $actions['sso_resend_invite'] = sprintf( - '%s', - add_query_arg( - array( - 'action' => 'jetpack_resend_invite_user_to_wpcom', - 'user_id' => $user_id, - 'resend_invite_nonce' => $nonce, - 'invite_id' => $has_pending_invite, - ), - admin_url( 'admin-post.php' ) - ), - esc_html__( 'Resend invite', 'jetpack' ) - ); - } - - if ( - current_user_can( 'promote_users' ) - && ( - $has_pending_invite - || Jetpack_SSO_Helpers::is_user_connected( $user_id ) - ) - ) { - unset( $actions['resetpassword'] ); - } - - return $actions; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->jetpack_user_table_row_actions' ); } /** * Render the invitation email message. + * + * @deprecated $$next-version$$ */ public function render_invitation_email_message() { - $message = wp_kses( - __( - 'We highly recommend inviting users to join WordPress.com and log in securely using Secure Sign On to ensure maximum security and efficiency.', - 'jetpack' - ), - array( - 'a' => array( - 'class' => array(), - 'href' => array(), - 'rel' => array(), - 'target' => array(), - ), - ) - ); - wp_admin_notice( - $message, - array( - 'id' => 'invitation_message', - 'type' => 'info', - 'dismissible' => false, - 'additional_classes' => array( 'jetpack-sso-admin-create-user-invite-message' ), - ) - ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->render_invitation_email_message' ); } /** * Render a note that wp.com invites will be automatically revoked. + * + * @deprecated $$next-version$$ */ public function render_invitations_notices_for_deleted_users() { - check_admin_referer( 'bulk-users' ); - - // When one user is deleted, the param is `user`, when multiple users are deleted, the param is `users`. - // We start with `users` and fallback to `user`. - $user_id = isset( $_GET['user'] ) ? intval( wp_unslash( $_GET['user'] ) ) : null; - $user_ids = isset( $_GET['users'] ) ? array_map( 'intval', wp_unslash( $_GET['users'] ) ) : array( $user_id ); - - $users_with_invites = array_filter( - $user_ids, - function ( $user_id ) { - return $user_id !== null && self::has_pending_wpcom_invite( $user_id ); - } - ); - - $users_with_invites = array_map( - function ( $user_id ) { - $user = get_user_by( 'id', $user_id ); - return $user->user_login; - }, - $users_with_invites - ); - - $invites_count = count( $users_with_invites ); - if ( $invites_count > 0 ) { - $users_with_invites = implode( ', ', $users_with_invites ); - $message = wp_kses( - sprintf( - /* translators: %s is a comma-separated list of user logins. */ - _n( - 'WordPress.com invitation will be automatically revoked for user: %s.', - 'WordPress.com invitations will be automatically revoked for users: %s.', - $invites_count, - 'jetpack' - ), - $users_with_invites - ), - array( 'strong' => true ) - ); - wp_admin_notice( - $message, - array( - 'id' => 'invitation_message', - 'type' => 'info', - 'dismissible' => false, - 'additional_classes' => array( 'jetpack-sso-admin-create-user-invite-message' ), - ) - ); - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->render_invitations_notices_for_deleted_users' ); } /** * Render WordPress.com invite checkbox for new user registration. * + * @deprecated $$next-version$$ + * * @param string $type The type of new user form the hook follows. */ public function render_wpcom_invite_checkbox( $type ) { - /* - * Only check this box by default on WordPress.com sites - * that do not use the WooCommerce plugin. - */ - $is_checked = ( new Host() )->is_wpcom_platform() && ! class_exists( 'WooCommerce' ); - - if ( $type === 'add-new-user' ) { - ?> - - - - - -
- - -
- - - - -
-
- render_wpcom_invite_checkbox' ); } /** * Render a checkbox to differentiate if a user is external. * + * @deprecated $$next-version$$ + * * @param string $type The type of new user form the hook follows. */ public function render_wpcom_external_user_checkbox( $type ) { - // Only enable this feature on WordPress.com sites. - if ( ! ( new Host() )->is_wpcom_platform() ) { - return; - } - - if ( $type === 'add-new-user' ) { - ?> - - - - - -
- - -
- - - - -
-
- render_wpcom_external_user_checkbox' ); } /** * Render the custom email message form field for new user registration. * + * @deprecated $$next-version$$ + * * @param string $type The type of new user form the hook follows. */ public function render_custom_email_message_form_field( $type ) { - if ( $type === 'add-new-user' ) { - $valid_nonce = isset( $_POST['_wpnonce_create-user'] ) - ? wp_verify_nonce( sanitize_key( $_POST['_wpnonce_create-user'] ), 'create-user' ) - : false; - $custom_email_message = ( $valid_nonce && isset( $_POST['custom_email_message'] ) ) ? sanitize_text_field( wp_unslash( $_POST['custom_email_message'] ) ) : ''; - ?> - - - - - -
- - -
- render_custom_email_message_form_field' ); } /** @@ -817,236 +172,36 @@ public function render_custom_email_message_form_field( $type ) { * It should be sent when SSO is disabled or when admins opt-out of WordPress.com invites intentionally. * If the "Send User Notification" checkbox is checked, the core invitation email should be sent. * - * @param boolean $send_wp_email Whether the core invitation email should be sent. + * @deprecated $$next-version$$ * - * @return boolean Indicating if the core invitation main should be sent. + * @param boolean $send_wp_email Whether the core invitation email should be sent. */ public function should_send_wp_mail_new_user( $send_wp_email ) { - if ( ! isset( $_POST['invite_user_wpcom'] ) && isset( $_POST['send_user_notification'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - return $send_wp_email; - } - return false; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->should_send_wp_mail_new_user' ); } /** * Send user invitation to WordPress.com if user has no errors. * + * @deprecated $$next-version$$ + * * @param WP_Error $errors The WP_Error object. * @param bool $update Whether the user is being updated or not. * @param stdClass $user The User object about to be created. - * @return WP_Error The modified or not WP_Error object. */ public function send_wpcom_mail_user_invite( $errors, $update, $user ) { - // Only admins should be able to invite new users. - if ( ! current_user_can( 'create_users' ) ) { - return $errors; - } - - if ( $update ) { - return $errors; - } - - // check for a valid nonce. - if ( - ! isset( $_POST['_wpnonce_create-user'] ) - || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce_create-user'] ), 'create-user' ) - ) { - return $errors; - } - - // Check if the user is being invited to WordPress.com. - if ( ! isset( $_POST['invite_user_wpcom'] ) ) { - return $errors; - } - - // check if the custom email message is too long. - if ( - ! empty( $_POST['custom_email_message'] ) - && strlen( sanitize_text_field( wp_unslash( $_POST['custom_email_message'] ) ) ) > 500 - ) { - $errors->add( - 'custom_email_message', - wp_kses( - __( 'Error: The custom message is too long. Please keep it under 500 characters.', 'jetpack' ), - array( - 'strong' => array(), - ) - ) - ); - } - - $site_id = Manager::get_site_id( true ); - if ( ! $site_id ) { - $errors->add( - 'invalid_site_id', - wp_kses( - __( 'Error: Invalid site ID.', 'jetpack' ), - array( - 'strong' => array(), - ) - ) - ); - } - - // Bail if there are any errors. - if ( $errors->has_errors() ) { - return $errors; - } - - $new_user_request = array( - 'email_or_username' => sanitize_email( $user->user_email ), - 'role' => sanitize_key( $user->role ), - ); - - if ( - isset( $_POST['custom_email_message'] ) - && strlen( sanitize_text_field( wp_unslash( $_POST['custom_email_message'] ) ) > 0 ) - ) { - $new_user_request['message'] = sanitize_text_field( wp_unslash( $_POST['custom_email_message'] ) ); - } - - if ( isset( $_POST['user_external_contractor'] ) ) { - $new_user_request['is_external'] = true; - } - - $response = Client::wpcom_json_api_request_as_user( - sprintf( - '/sites/%d/invites/new', - (int) $site_id - ), - '2', // Api version - array( - 'method' => 'POST', - ), - array( - 'invitees' => array( $new_user_request ), - ) - ); - - $event_name = 'sso_new_user_invite_sent'; - $custom_message_sent = isset( $new_user_request['message'] ) ? 'true' : 'false'; - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - $errors->add( - 'invitation_not_sent', - wp_kses( - __( 'Error: The user invitation email could not be sent, the user account was not created.', 'jetpack' ), - array( - 'strong' => array(), - ) - ) - ); - self::$tracking->record_user_event( - $event_name, - array( - 'success' => 'false', - 'error' => wp_remote_retrieve_body( $response ), // Get as much information as possible. - ) - ); - } else { - self::$tracking->record_user_event( - $event_name, - array( - 'success' => 'true', - 'custom_message_sent' => $custom_message_sent, - ) - ); - } - - return $errors; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->send_wpcom_mail_user_invite' ); } /** * Adds a column in the user admin table to display user connection status and actions. * - * @param array $columns User list table columns. + * @deprecated $$next-version$$ * - * @return array + * @param array $columns User list table columns. */ public function jetpack_user_connected_th( $columns ) { - - $tooltip_string = esc_attr__( 'Jetpack SSO allows a seamless and secure experience on WordPress.com. Join millions of WordPress users who trust us to keep their accounts safe.', 'jetpack' ); - - wp_enqueue_script( - 'jetpack-sso-users', - plugins_url( 'jetpack_vendor/automattic/jetpack-connection/src/sso/jetpack-sso-users.js', JETPACK__PLUGIN_FILE ), - array(), - JETPACK__VERSION, - false - ); - - wp_add_inline_script( - 'jetpack-sso-users', - "var Jetpack_SSOTooltip = { 'tooltipString': '{$tooltip_string}' }", - 'before' - ); - - $columns['user_jetpack'] = sprintf( - '%2$s', - $tooltip_string, - esc_html__( 'SSO Status', 'jetpack' ), - esc_attr__( 'Tooltip', 'jetpack' ) - ); - return $columns; - } - - /** - * Executed when our WP_User_Query instance is set, and we don't have cached invites. - * This function uses the user emails and the 'are-users-invited' endpoint to build the cache. - * - * @return void - */ - private static function rebuild_invite_cache() { - $blog_id = Manager::get_site_id( true ); - - if ( self::$cached_invites === null && self::$user_search !== null ) { - - self::$cached_invites = array(); - - $results = self::$user_search->get_results(); - - $user_emails = array_reduce( - $results, - function ( $current, $item ) { - if ( ! Jetpack_SSO_Helpers::is_user_connected( $item->ID ) ) { - $current[] = rawurlencode( $item->user_email ); - } else { - self::$cached_invites[] = array( - 'email_or_username' => $item->user_email, - 'invited' => false, - 'invite_code' => '', - ); - } - return $current; - }, - array() - ); - - if ( ! empty( $user_emails ) ) { - $url = '/sites/' . $blog_id . '/invites/are-users-invited'; - - $response = Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array( - 'method' => 'POST', - ), - array( 'users' => $user_emails ), - 'wpcom' - ); - - if ( 200 === wp_remote_retrieve_response_code( $response ) ) { - $body = json_decode( $response['body'], true ); - - // ensure array_merge happens with the right parameters - if ( empty( $body ) ) { - $body = array(); - } - - self::$cached_invites = array_merge( self::$cached_invites, $body ); - } - } - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->jetpack_user_connected_th' ); } /** @@ -1055,242 +210,45 @@ function ( $current, $item ) { * @access private * @static * - * @param string $email The user email. + * @deprecated $$next-version$$ * - * @return array|void Returns the cached invite if found. + * @param string $email The user email. */ public static function get_pending_cached_wpcom_invite( $email ) { - if ( self::$cached_invites === null ) { - self::rebuild_invite_cache(); - } - - if ( ! empty( self::$cached_invites ) && is_array( self::$cached_invites ) ) { - $index = array_search( $email, array_column( self::$cached_invites, 'email_or_username' ), true ); - if ( $index !== false ) { - return self::$cached_invites[ $index ]; - } - } - } - - /** - * Check if a given user is invited to the site. - * - * @access private - * @static - * @param int $user_id The user ID. - * - * @return string|false returns the user invite code if the user is invited, false otherwise. - */ - private static function has_pending_wpcom_invite( $user_id ) { - $blog_id = Manager::get_site_id( true ); - $user = get_user_by( 'id', $user_id ); - $cached_invite = self::get_pending_cached_wpcom_invite( $user->user_email ); - - if ( $cached_invite ) { - return $cached_invite['invite_code']; - } - - $url = '/sites/' . $blog_id . '/invites/is-invited'; - $url = add_query_arg( - array( - 'email_or_username' => rawurlencode( $user->user_email ), - ), - $url - ); - $response = Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array(), - null, - 'wpcom' - ); - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - return false; - } - - return json_decode( $response['body'], true )['invite_code']; - } - - /** - * Delete an external contributor from the site. - * - * @access private - * @static - * @param int $user_id The user ID. - * - * @return bool Returns true if the user was successfully deleted, false otherwise. - */ - private static function delete_external_contributor( $user_id ) { - $blog_id = Manager::get_site_id( true ); - $url = '/sites/' . $blog_id . '/external-contributors/remove'; - $response = Client::wpcom_json_api_request_as_user( - $url, - 'v2', - array( - 'method' => 'POST', - ), - array( - 'user_id' => $user_id, - ), - 'wpcom' - ); - - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - return false; - } - - return true; + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->get_pending_cached_wpcom_invite' ); } /** * Show Jetpack SSO user connection status. * + * @deprecated $$next-version$$ + * * @param string $val HTML for the column. * @param string $col User list table column. * @param int $user_id User ID. - * - * @return string */ public function jetpack_show_connection_status( $val, $col, $user_id ) { - if ( 'user_jetpack' === $col ) { - if ( Jetpack_SSO_Helpers::is_user_connected( $user_id ) ) { - $connection_html = sprintf( - '%2$s', - esc_attr__( 'This user is connected and can log-in to this site.', 'jetpack' ), - esc_html__( 'Connected', 'jetpack' ) - ); - return $connection_html; - } else { - $has_pending_invite = self::has_pending_wpcom_invite( $user_id ); - if ( $has_pending_invite ) { - $connection_html = sprintf( - '%2$s', - esc_attr__( 'This user didn’t accept the invitation to join this site yet.', 'jetpack' ), - esc_html__( 'Pending invite', 'jetpack' ) - ); - return $connection_html; - } - $nonce = wp_create_nonce( 'jetpack-sso-invite-user' ); - $connection_html = sprintf( - // Using formmethod and formaction because we can't nest forms and have to submit using the main form. - ' - %2$s - - %3$s - - ', - add_query_arg( - array( - 'user_id' => $user_id, - 'invite_nonce' => $nonce, - 'action' => 'jetpack_invite_user_to_wpcom', - ), - admin_url( 'admin-post.php' ) - ), - esc_html__( 'Send invite', 'jetpack' ), - esc_attr__( 'This user doesn’t have an SSO connection to WordPress.com. Invite them to the site to increase security and improve their experience.', 'jetpack' ), - esc_attr__( 'Tooltip', 'jetpack' ) - ); - return $connection_html; - } - } + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->jetpack_show_connection_status' ); } /** * Creates error notices and redirects the user to the previous page. * + * @deprecated $$next-version$$ + * * @param array $query_params - query parameters added to redirection URL. */ public function create_error_notice_and_redirect( $query_params ) { - $ref = wp_get_referer(); - if ( empty( $ref ) ) { - $ref = network_admin_url( 'users.php' ); - } - - $url = add_query_arg( - $query_params, - $ref - ); - return wp_safe_redirect( $url ); + _deprecated_function( __METHOD__, 'jetpack-$$next-version$$', 'Automattic\\Jetpack\\Connection\\SSO\\User_Admin->create_error_notice_and_redirect' ); } /** * Style the Jetpack user rows and columns. + * + * @deprecated $$next-version$$ */ public function jetpack_user_table_styles() { - ?> - - jetpack_user_table_styles' ); } } endif; diff --git a/projects/plugins/jetpack/phpunit.xml.dist b/projects/plugins/jetpack/phpunit.xml.dist index 68b4c81e47bce..099467c668424 100644 --- a/projects/plugins/jetpack/phpunit.xml.dist +++ b/projects/plugins/jetpack/phpunit.xml.dist @@ -51,9 +51,6 @@ tests/php/modules/sitemaps - - tests/php/modules/sso - tests/php/modules/subscriptions diff --git a/projects/plugins/jetpack/tests/php.multisite.xml b/projects/plugins/jetpack/tests/php.multisite.xml index 2aaaf3ab7d12e..c0e91fdaf0656 100644 --- a/projects/plugins/jetpack/tests/php.multisite.xml +++ b/projects/plugins/jetpack/tests/php.multisite.xml @@ -43,9 +43,6 @@ php/modules/widgets - - php/modules/sso - php/modules/subscriptions diff --git a/projects/plugins/jetpack/tests/php/bootstrap.php b/projects/plugins/jetpack/tests/php/bootstrap.php index b051c86565812..1ca37dc618a42 100644 --- a/projects/plugins/jetpack/tests/php/bootstrap.php +++ b/projects/plugins/jetpack/tests/php/bootstrap.php @@ -151,6 +151,11 @@ function jetpack_full_sync_immediately_off( $modules ) { require __DIR__ . '/../../modules/shortcodes.php'; } +// Load the sso module to test properly. +if ( ! in_running_uninstall_group() ) { + require __DIR__ . '/../../modules/sso.php'; +} + // Load attachment helper methods. require __DIR__ . '/attachment_test_case.php'; diff --git a/projects/plugins/jetpack/tests/php/general/test_class.jetpack.php b/projects/plugins/jetpack/tests/php/general/test_class.jetpack.php index 48c597f541400..b99202f86a501 100644 --- a/projects/plugins/jetpack/tests/php/general/test_class.jetpack.php +++ b/projects/plugins/jetpack/tests/php/general/test_class.jetpack.php @@ -863,6 +863,7 @@ public function test_classic_xmlrpc_when_active_and_signed_with_user() { 'jetpack.subscriptions.subscribe', 'jetpack.updatePublicizeConnections', 'jetpack.getHeartbeatData', + 'jetpack.userDisconnect', ); $this->assertXMLRPCMethodsComply( $required, $allowed, array_keys( $methods ) ); @@ -919,6 +920,7 @@ public function test_classic_xmlrpc_when_active_and_signed_with_user_with_edit() 'jetpack.subscriptions.subscribe', 'jetpack.updatePublicizeConnections', 'jetpack.getHeartbeatData', + 'jetpack.userDisconnect', ); $this->assertXMLRPCMethodsComply( $required, $allowed, array_keys( $methods ) ); diff --git a/projects/plugins/jetpack/tests/php/modules/sso/test_class.jetpack-sso-helpers.php b/projects/plugins/jetpack/tests/php/modules/sso/test_class.jetpack-sso-helpers.php deleted file mode 100644 index 070aca6b6dba2..0000000000000 --- a/projects/plugins/jetpack/tests/php/modules/sso/test_class.jetpack-sso-helpers.php +++ /dev/null @@ -1,407 +0,0 @@ -user_data = (object) array( - 'ID' => 123456789, - 'email' => 'ssouser@testautomattic.com', - 'login' => 'ssouser', - 'display_name' => 'ssouser', - 'first_name' => 'sso', - 'last_name' => 'user', - 'url' => 'https://automattic.com', - 'description' => 'A user to test SSO', - ); - } - - /** - * Return 1. - * - * @return int - */ - public function return_one() { - return 1; - } - - /** - * Test "sso_helpers_is_two_step_required_filter_true". - */ - public function test_sso_helpers_is_two_step_required_filter_true() { - add_filter( 'jetpack_sso_require_two_step', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::is_two_step_required() ); - remove_filter( 'jetpack_sso_require_two_step', '__return_true' ); - } - - /** - * Test "sso_helpers_is_two_step_required_filter_false". - */ - public function test_sso_helpers_is_two_step_required_filter_false() { - add_filter( 'jetpack_sso_require_two_step', '__return_false' ); - $this->assertFalse( Jetpack_SSO_Helpers::is_two_step_required() ); - remove_filter( 'jetpack_sso_require_two_step', '__return_false' ); - } - - /** - * Test "sso_helpers_is_two_step_required_option_true". - */ - public function test_sso_helpers_is_two_step_required_option_true() { - update_option( 'jetpack_sso_require_two_step', true ); - $this->assertTrue( Jetpack_SSO_Helpers::is_two_step_required() ); - delete_option( 'jetpack_sso_require_two_step' ); - } - - /** - * Test "sso_helpers_is_two_step_required_option_false". - */ - public function test_sso_helpers_is_two_step_required_option_false() { - update_option( 'jetpack_sso_require_two_step', false ); - $this->assertFalse( Jetpack_SSO_Helpers::is_two_step_required() ); - delete_option( 'jetpack_sso_require_two_step' ); - } - - /** - * Test "sso_helpers_should_hide_login_form_filter_true". - */ - public function test_sso_helpers_should_hide_login_form_filter_true() { - add_filter( 'jetpack_remove_login_form', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::should_hide_login_form() ); - remove_filter( 'jetpack_remove_login_form', '__return_true' ); - } - - /** - * Test "sso_helpers_should_hide_login_form_filter_false". - */ - public function test_sso_helpers_should_hide_login_form_filter_false() { - add_filter( 'jetpack_remove_login_form', '__return_false' ); - $this->assertFalse( Jetpack_SSO_Helpers::should_hide_login_form() ); - remove_filter( 'jetpack_remove_login_form', '__return_false' ); - } - - /** - * Test "sso_helpers_match_by_email_filter_true". - */ - public function test_sso_helpers_match_by_email_filter_true() { - add_filter( 'jetpack_sso_match_by_email', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::match_by_email() ); - remove_filter( 'jetpack_sso_match_by_email', '__return_true' ); - } - - /** - * Test "sso_helpers_match_by_email_filter_false". - */ - public function test_sso_helpers_match_by_email_filter_false() { - add_filter( 'jetpack_sso_match_by_email', '__return_false' ); - $this->assertFalse( Jetpack_SSO_Helpers::match_by_email() ); - remove_filter( 'jetpack_sso_match_by_email', '__return_false' ); - } - - /** - * Test "sso_helpers_new_user_override_filter_true_returns_default_role". - */ - public function test_sso_helpers_new_user_override_filter_true_returns_default_role() { - add_role( 'foo', 'Foo' ); - update_option( 'default_role', 'foo' ); - add_filter( 'jetpack_sso_new_user_override', '__return_true' ); - $this->assertEquals( 'foo', Jetpack_SSO_Helpers::new_user_override() ); - remove_filter( 'jetpack_sso_new_user_override', '__return_true' ); - } - - /** - * Test "sso_helpers_new_user_override_filter_false". - */ - public function test_sso_helpers_new_user_override_filter_false() { - add_filter( 'jetpack_sso_new_user_override', '__return_false' ); - $this->assertFalse( Jetpack_SSO_Helpers::new_user_override() ); - remove_filter( 'jetpack_sso_new_user_override', '__return_false' ); - } - - /** - * Test "sso_helpers_new_user_override_filter_rolename". - */ - public function test_sso_helpers_new_user_override_filter_rolename() { - add_filter( 'jetpack_sso_new_user_override', array( $this, 'return_administrator' ) ); - $this->assertEquals( 'administrator', Jetpack_SSO_Helpers::new_user_override() ); - remove_filter( 'jetpack_sso_new_user_override', array( $this, 'return_administrator' ) ); - } - - /** - * Test "sso_helpers_new_user_override_filter_bad_rolename_returns_default". - */ - public function test_sso_helpers_new_user_override_filter_bad_rolename_returns_default() { - add_role( 'foo', 'Foo' ); - update_option( 'default_role', 'foo' ); - add_filter( 'jetpack_sso_new_user_override', array( $this, 'return_foobarbaz' ) ); - $this->assertEquals( 'foo', Jetpack_SSO_Helpers::new_user_override() ); - remove_filter( 'jetpack_sso_new_user_override', array( $this, 'return_foobarbaz' ) ); - } - - /** - * Test "sso_helpers_sso_bypass_default_login_form_filter_true". - */ - public function test_sso_helpers_sso_bypass_default_login_form_filter_true() { - add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::bypass_login_forward_wpcom() ); - remove_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true' ); - } - - /** - * Test "sso_helpers_sso_bypass_default_login_form_filter_false". - */ - public function test_sso_helpers_sso_bypass_default_login_form_filter_false() { - add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_false' ); - $this->assertFalse( Jetpack_SSO_Helpers::bypass_login_forward_wpcom() ); - remove_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_false' ); - } - - /** - * Test "sso_helpers_require_two_step_disabled". - */ - public function test_sso_helpers_require_two_step_disabled() { - add_filter( 'jetpack_sso_require_two_step', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); - remove_filter( 'jetpack_sso_require_two_step', '__return_true' ); - } - - /** - * Test "sso_helpers_require_two_step_enabled". - */ - public function test_sso_helpers_require_two_step_enabled() { - $this->assertFalse( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); - } - - /** - * Test "sso_helpers_match_by_email_disabled". - */ - public function test_sso_helpers_match_by_email_disabled() { - add_filter( 'jetpack_sso_match_by_email', '__return_true' ); - $this->assertTrue( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); - remove_filter( 'jetpack_sso_match_by_email', '__return_true' ); - } - - /** - * Test "sso_helpers_match_by_email_enabled". - */ - public function test_sso_helpers_match_by_email_enabled() { - $this->assertFalse( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); - } - - /** - * Test "allow_redirect_hosts_adds_default_hosts". - */ - public function test_allow_redirect_hosts_adds_default_hosts() { - $hosts = Jetpack_SSO_Helpers::allowed_redirect_hosts( array( 'test.com' ) ); - $this->assertIsArray( $hosts ); - $this->assertContains( 'test.com', $hosts ); - $this->assertContains( 'wordpress.com', $hosts ); - $this->assertContains( 'jetpack.wordpress.com', $hosts ); - } - - /** - * Test "allowed_redirect_hosts_api_base_added". - */ - public function test_allowed_redirect_hosts_api_base_added() { - $hosts = Jetpack_SSO_Helpers::allowed_redirect_hosts( - array( 'test.com' ), - 'http://fakesite.com/jetpack.' - ); - $this->assertIsArray( $hosts ); - $this->assertCount( 6, $hosts ); - $this->assertContains( 'fakesite.com', $hosts ); - } - - /** - * Test "allowed_redirect_hosts_api_base_added_on_dev_version". - */ - public function test_allowed_redirect_hosts_api_base_added_on_dev_version() { - add_filter( 'jetpack_development_version', '__return_true' ); - $hosts = Jetpack_SSO_Helpers::allowed_redirect_hosts( - array( 'test.com' ), - 'http://fakesite.com/jetpack.' - ); - $this->assertIsArray( $hosts ); - $this->assertCount( 6, $hosts ); - $this->assertContains( 'fakesite.com', $hosts ); - remove_filter( 'jetpack_development_version', '__return_true' ); - } - - /** - * Test "generate_user_returns_user_when_username_not_exists". - */ - public function test_generate_user_returns_user_when_username_not_exists() { - $user = Jetpack_SSO_Helpers::generate_user( $this->user_data ); - $this->assertIsObject( $user ); - $this->assertInstanceOf( 'WP_User', $user ); - - wp_delete_user( $user->ID ); - } - - /** - * Test "generate_user_returns_user_if_username_exists_and_has_tries". - */ - public function test_generate_user_returns_user_if_username_exists_and_has_tries() { - add_filter( 'jetpack_sso_allowed_username_generate_retries', array( $this, 'return_one' ) ); - self::factory()->user->create( array( 'user_login' => $this->user_data->login ) ); - - $user = Jetpack_SSO_Helpers::generate_user( $this->user_data ); - - $this->assertIsObject( $user ); - $this->assertInstanceOf( 'WP_User', $user ); - - // If the username contains the user's ID, we know the username was generated with our random algo. - $this->assertStringContainsString( (string) $this->user_data->ID, $user->user_login ); - - wp_delete_user( $user->ID ); - } - - /** - * Test "generate_user_returns_false_when_no_more_tries_and_username_exists". - */ - public function test_generate_user_returns_false_when_no_more_tries_and_username_exists() { - add_filter( 'jetpack_sso_allowed_username_generate_retries', '__return_zero' ); - self::factory()->user->create( array( 'user_login' => $this->user_data->login ) ); - $this->assertFalse( Jetpack_SSO_Helpers::generate_user( $this->user_data ) ); - } - - /** - * Test "generate_user_sets_user_role_when_provided". - */ - public function test_generate_user_sets_user_role_when_provided() { - $this->user_data->role = 'administrator'; - $user = Jetpack_SSO_Helpers::generate_user( $this->user_data ); - $this->assertContains( 'administrator', get_userdata( $user->ID )->roles ); - } - - /** - * Test "extend_auth_cookie_casts_to_int". - */ - public function test_extend_auth_cookie_casts_to_int() { - add_filter( 'jetpack_sso_auth_cookie_expiration', array( $this, 'return_string_value' ) ); - $this->assertSame( (int) $this->return_string_value(), Jetpack_SSO_Helpers::extend_auth_cookie_expiration_for_sso() ); - remove_filter( 'jetpack_sso_auth_cookie_expiration', array( $this, 'return_string_value' ) ); - } - - /** - * Test "extend_auth_cookie_default_value_greater_than_default". - */ - public function test_extend_auth_cookie_default_value_greater_than_default() { - $this->assertGreaterThan( 2 * DAY_IN_SECONDS, Jetpack_SSO_Helpers::extend_auth_cookie_expiration_for_sso() ); - } - - /** - * Test "display_sso_form_for_action". - */ - public function test_display_sso_form_for_action() { - // Let's test the default cases. - $this->assertTrue( Jetpack_SSO_Helpers::display_sso_form_for_action( 'login' ) ); - $this->assertTrue( Jetpack_SSO_Helpers::display_sso_form_for_action( 'jetpack_json_api_authorization' ) ); - $this->assertFalse( Jetpack_SSO_Helpers::display_sso_form_for_action( 'hello_world' ) ); - - add_filter( 'jetpack_sso_allowed_actions', array( $this, 'allow_hello_world_login_action_for_sso' ) ); - $this->assertTrue( Jetpack_SSO_Helpers::display_sso_form_for_action( 'hello_world' ) ); - remove_filter( 'jetpack_sso_allowed_actions', array( $this, 'allow_hello_world_login_action_for_sso' ) ); - } - - /** - * Test "get_json_api_auth_environment". - */ - public function test_get_json_api_auth_environment() { - // With no cookie returns false. - $_COOKIE['jetpack_sso_original_request'] = ''; - $this->assertFalse( Jetpack_SSO_Helpers::get_json_api_auth_environment() ); - - // With empty query, returns false. - $_COOKIE['jetpack_sso_original_request'] = 'http://website.com'; - $this->assertFalse( Jetpack_SSO_Helpers::get_json_api_auth_environment() ); - - // With empty no action query argument, returns false. - $_COOKIE['jetpack_sso_original_request'] = 'http://website.com?hello=world'; - $this->assertFalse( Jetpack_SSO_Helpers::get_json_api_auth_environment() ); - - // When action is not for JSON API auth, return false. - $_COOKIE['jetpack_sso_original_request'] = 'http://website.com?action=loggedout'; - $this->assertFalse( Jetpack_SSO_Helpers::get_json_api_auth_environment() ); - - // If we pass the other tests, then let's make sure we get the right information back. - $original_request = 'http://website.com/wp-login.php?action=jetpack_json_api_authorization&token=my-token'; - $_COOKIE['jetpack_sso_original_request'] = $original_request; - $environment = Jetpack_SSO_Helpers::get_json_api_auth_environment(); - $this->assertIsArray( $environment ); - $this->assertSame( - array( - 'action' => 'jetpack_json_api_authorization', - 'token' => 'my-token', - 'jetpack_json_api_original_query' => $original_request, - ), - $environment - ); - } - - /** - * Test the `get_custom_login_url()` helper. - */ - public function test_get_custom_login_url() { - $login_url_default = Jetpack_SSO_Helpers::get_custom_login_url(); - - $custom_url_expected = 'test-login-url/'; - - $custom_url_filter = function ( $login_url ) use ( $custom_url_expected ) { - return str_replace( 'wp-login.php', $custom_url_expected, $login_url ); - }; - add_filter( 'login_url', $custom_url_filter ); - $login_url_custom = Jetpack_SSO_Helpers::get_custom_login_url(); - - static::assertNull( $login_url_default ); - static::assertEquals( $custom_url_expected, $login_url_custom ); - } - - /** - * Return string '1'. - * - * @return string - */ - public function return_string_value() { - return '1'; - } - - /** - * Return "administrator". - * - * @return string - */ - public function return_administrator() { - return 'administrator'; - } - - /** - * Return "foobarbaz". - * - * @return string - */ - public function return_foobarbaz() { - return 'foobarbaz'; - } - - /** - * Add "hello_world" action. - * - * @param array $actions Actions. - * @return array - */ - public function allow_hello_world_login_action_for_sso( $actions ) { - $actions[] = 'hello_world'; - return $actions; - } -} diff --git a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-callables.php b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-callables.php index 616435a87e5fb..bf8a49bfd02a3 100644 --- a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-callables.php +++ b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-callables.php @@ -2,6 +2,7 @@ use Automattic\Jetpack\Blocks; use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication; +use Automattic\Jetpack\Connection\SSO\Helpers; use Automattic\Jetpack\Connection\Urls; use Automattic\Jetpack\Constants; use Automattic\Jetpack\Sync\Defaults; @@ -116,11 +117,11 @@ public function test_sync_callable_whitelist() { 'post_type_features' => Functions::get_post_type_features(), 'rest_api_allowed_post_types' => Functions::rest_api_allowed_post_types(), 'rest_api_allowed_public_metadata' => Functions::rest_api_allowed_public_metadata(), - 'sso_is_two_step_required' => Jetpack_SSO_Helpers::is_two_step_required(), - 'sso_should_hide_login_form' => Jetpack_SSO_Helpers::should_hide_login_form(), - 'sso_match_by_email' => Jetpack_SSO_Helpers::match_by_email(), - 'sso_new_user_override' => Jetpack_SSO_Helpers::new_user_override(), - 'sso_bypass_default_login_form' => Jetpack_SSO_Helpers::bypass_login_forward_wpcom(), + 'sso_is_two_step_required' => Helpers::is_two_step_required(), + 'sso_should_hide_login_form' => Helpers::should_hide_login_form(), + 'sso_match_by_email' => Helpers::match_by_email(), + 'sso_new_user_override' => Helpers::new_user_override(), + 'sso_bypass_default_login_form' => Helpers::bypass_login_forward_wpcom(), 'wp_version' => Functions::wp_version(), 'get_plugins' => Functions::get_plugins(), 'get_plugins_action_links' => Functions::get_plugins_action_links(), diff --git a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-users.php b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-users.php index 590f40be76815..cb4f828597bb3 100644 --- a/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-users.php +++ b/projects/plugins/jetpack/tests/php/sync/test_class.jetpack-sync-users.php @@ -1,5 +1,6 @@ get_invite_user_data() ); + Utils::generate_user( $this->get_invite_user_data() ); $this->sender->do_sync(); Constants::clear_constants(); @@ -750,7 +751,7 @@ public function test_invite_user_sync_invite_event_false() { // Fake it till we make it Constants::set_constant( 'JETPACK_INVITE_ACCEPTED', false ); // We modify the input here to mimick the same call structure of the update user endpoint. - Jetpack_SSO_Helpers::generate_user( $this->get_invite_user_data() ); + Utils::generate_user( $this->get_invite_user_data() ); $this->sender->do_sync(); Constants::clear_constants();