diff --git a/projects/packages/account-protection/src/assets/jetpack-logo.svg b/projects/packages/account-protection/src/assets/jetpack-logo.svg new file mode 100644 index 0000000000000..b91e3c5c216f5 --- /dev/null +++ b/projects/packages/account-protection/src/assets/jetpack-logo.svg @@ -0,0 +1,21 @@ + + "Jetpack Logo" + + + + + + + + + diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index 6ef5c215d4fdd..a1c92f9c49093 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -13,35 +13,94 @@ * Class Account_Protection */ class Account_Protection { - const PACKAGE_VERSION = '1.0.0-alpha'; const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection'; const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode'; + /** + * Modules dependency. + * + * @var Modules + */ + private $modules; + + /** + * Password Detection dependency. + * + * @var Password_Detection + */ + private $password_detection; + + /** + * Constructor. + * + * @param Modules|null $modules Modules dependency. + * @param Password_Detection|null $password_detection Password detection dependency. + */ + public function __construct( Modules $modules = null, Password_Detection $password_detection = null ) { + $this->modules = $modules ?? new Modules(); + $this->password_detection = $password_detection ?? new Password_Detection(); + } + /** * Initializes the configurations needed for the account protection module. */ - public static function init() { + public function init(): void { + $this->register_hooks(); + + if ( $this->is_enabled() ) { + $this->register_runtime_hooks(); + } + } + + /** + * Register hooks for module activation and environment validation. + */ + private function register_hooks(): void { // Account protection activation/deactivation hooks - add_action( 'jetpack_activate_module_account-protection', __CLASS__ . '::on_account_protection_activation' ); - add_action( 'jetpack_deactivate_module_account-protection', __CLASS__ . '::on_account_protection_deactivation' ); + add_action( 'jetpack_activate_module_' . self::ACCOUNT_PROTECTION_MODULE_NAME, array( $this, 'on_account_protection_activation' ) ); + add_action( 'jetpack_deactivate_module_' . self::ACCOUNT_PROTECTION_MODULE_NAME, array( $this, 'on_account_protection_deactivation' ) ); + + // Do not run in unsupported environments + add_action( 'jetpack_get_available_modules', array( $this, 'remove_module_on_unsupported_environments' ) ); + add_action( 'jetpack_get_available_standalone_modules', array( $this, 'remove_standalone_module_on_unsupported_environments' ) ); // Register REST routes add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) ); } + /** + * Register hooks for runtime operations. + */ + private function register_runtime_hooks(): void { + // Validate password after successful login + add_action( 'wp_authenticate_user', array( $this->password_detection, 'login_form_password_detection' ), 10, 2 ); + + // Add password detection flow + add_action( 'login_form_password-detection', array( $this->password_detection, 'render_page' ), 10, 2 ); + + // Remove password detection usermeta after password reset and on profile password update + add_action( 'after_password_reset', array( $this->password_detection, 'delete_usermeta_after_password_reset' ), 10, 2 ); + add_action( 'profile_update', array( $this->password_detection, 'delete_usermeta_on_profile_update' ), 10, 2 ); + + // Register AJAX resend password reset email action + add_action( 'wp_ajax_resend_password_reset', array( $this->password_detection, 'ajax_resend_password_reset_email' ) ); + } + /** * Activate the account protection on module activation. */ - public static function on_account_protection_activation() { - // Account protection activated + public function on_account_protection_activation(): void { + // Activation logic can be added here } /** - * Deactivate the account protection on module activation. + * Deactivate the account protection on module deactivation. */ - public static function on_account_protection_deactivation() { - // Account protection deactivated + public function on_account_protection_deactivation(): void { + // Remove password detection user meta on deactivation + // TODO: Run on Jetpack and Protect deactivation + $this->password_detection->delete_all_usermeta(); } /** @@ -49,8 +108,8 @@ public static function on_account_protection_deactivation() { * * @return bool */ - public static function is_enabled() { - return ( new Modules() )->is_active( 'account-protection' ); + public function is_enabled() { + return $this->modules->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); } /** @@ -58,12 +117,12 @@ public static function is_enabled() { * * @return bool */ - public static function enable() { + public function enable() { // Return true if already enabled. - if ( self::is_enabled() ) { + if ( $this->is_enabled() ) { return true; } - return ( new Modules() )->activate( 'account-protection', false, false ); + return $this->modules->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); } /** @@ -71,12 +130,64 @@ public static function enable() { * * @return bool */ - public static function disable() { + public function disable(): bool { // Return true if already disabled. - if ( ! self::is_enabled() ) { + if ( ! $this->is_enabled() ) { return true; } - return ( new Modules() )->deactivate( 'account-protection' ); + return $this->modules->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); + } + + /** + * Determines if Account Protection is supported in the current environment. + * + * @return bool + */ + public function is_supported_environment(): bool { + // Do not run when killswitch is enabled + if ( defined( 'DISABLE_JETPACK_ACCOUNT_PROTECTION' ) && DISABLE_JETPACK_ACCOUNT_PROTECTION ) { + return false; + } + + return true; + } + + /** + * Disables the Account Protection module when on an unsupported platform in Jetpack. + * + * @param array $modules Filterable value for `jetpack_get_available_modules`. + * + * @return array Array of module slugs. + */ + public function remove_module_on_unsupported_environments( array $modules ): array { + if ( ! $this->is_supported_environment() ) { + // Account protection should never be available on unsupported platforms. + unset( $modules[ self::ACCOUNT_PROTECTION_MODULE_NAME ] ); + } + + return $modules; + } + + /** + * Disables the Account Protection module when on an unsupported platform in a standalone plugin. + * + * @param array $modules Filterable value for `jetpack_get_available_standalone_modules`. + * + * @return array Array of module slugs. + */ + public function remove_standalone_module_on_unsupported_environments( array $modules ): array { + if ( ! $this->is_supported_environment() ) { + // Account Protection should never be available on unsupported platforms. + $modules = array_filter( + $modules, + function ( $module ) { + return $module !== self::ACCOUNT_PROTECTION_MODULE_NAME; + } + ); + + } + + return $modules; } /** @@ -84,7 +195,7 @@ public static function disable() { * * @return array */ - public static function get_settings() { + public function get_settings(): array { $settings = array( self::STRICT_MODE_OPTION_NAME => get_option( self::STRICT_MODE_OPTION_NAME, false ), ); diff --git a/projects/packages/account-protection/src/class-password-detection.php b/projects/packages/account-protection/src/class-password-detection.php new file mode 100644 index 0000000000000..c6e39b533a439 --- /dev/null +++ b/projects/packages/account-protection/src/class-password-detection.php @@ -0,0 +1,392 @@ +password_reset_email = $password_reset_email ?? new Password_Reset_Email(); + } + + /** + * Redirect to the password detection page. + * + * @return string The URL to redirect to. + */ + public function password_detection_redirect(): string { + return home_url( '/wp-login.php?action=password-detection' ); + } + + /** + * Check if the password is safe after login. + * + * @param \WP_User $user The user object. + * @param string $password The password. + * @return \WP_User|\WP_Error The user object. + */ + public function login_form_password_detection( \WP_User $user, string $password ): \WP_User { + // Check if the user is already a WP_Error object + if ( is_wp_error( $user ) ) { + return $user; + } + + // Ensure the password is correct for this user + if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) { + return $user; + } + + if ( ! $this->validate_password( $password ) ) { + // TODO: Ensure usermeta is always up to date + $this->update_usermeta( $user->ID, 'unsafe' ); + + // Redirect to the password detection page + add_filter( 'login_redirect', array( $this, 'password_detection_redirect' ), 10, 3 ); + } else { + $this->update_usermeta( $user->ID, 'safe' ); + } + + return $user; + } + + /** + * Render password detection page. + * + * @return void + */ + public function render_page(): void { + // Restrict direct access to logged in users + $current_user = wp_get_current_user(); + if ( 0 === $current_user->ID ) { + wp_safe_redirect( wp_login_url() ); + exit; + } + + // Restrict direct access to users with unsafe passwords + $user_password_status = $this->get_usermeta( $current_user->ID ); + if ( ! $user_password_status || 'safe' === $user_password_status ) { + wp_safe_redirect( admin_url() ); + exit; + } + + // Use a transient to track email sent status + $transient_key = 'password_reset_email_sent_' . $current_user->ID; + $email_sent_flag = get_transient( $transient_key ); + + // Initialize template variables + $reset = false; + $context = 'Your current password was found in a public leak, which means your account might be at risk.'; + $error = ''; + + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) ); + + // Handle reset_password_action form submission + if ( isset( $_POST['reset-password'] ) ) { + $reset = true; + + // Verify nonce + if ( isset( $_POST['_wpnonce_reset_password'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce_reset_password'] ) ), 'reset_password_action' ) ) { + // Send password reset email + if ( ! $email_sent_flag ) { + $email_sent = $this->password_reset_email->send( $current_user ); + if ( $email_sent ) { + // Set transient to mark the email as sent + set_transient( $transient_key, true, 15 * MINUTE_IN_SECONDS ); + } else { + $error = 'email_send_error'; + } + } + + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_resend_password_reset_scripts' ) ); + } else { + $error = 'reset_passowrd_nonce_verification_error'; + } + + // Handle proceed_action form submission + } elseif ( isset( $_POST['proceed'] ) ) { + $reset = true; + + // Verify nonce + if ( isset( $_POST['_wpnonce_proceed'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce_proceed'] ) ), 'proceed_action' ) ) { + wp_safe_redirect( admin_url() ); + exit; + } else { + $error = 'proceed_nonce_verification_error'; + } + } + + $this->render_content( $reset, $context, $error, $this->password_reset_email->mask_email_address( $current_user->user_email ) ); + exit; + } + + /** + * Enqueue the resend password reset email scripts. + * + * @return void + */ + public function enqueue_resend_password_reset_scripts(): void { + wp_enqueue_script( 'resend-password-reset', plugin_dir_url( __FILE__ ) . 'js/resend-password-reset.js', array( 'jquery' ), Account_Protection::PACKAGE_VERSION, true ); + + // Pass AJAX URL and nonce to the script + wp_localize_script( + 'resend-password-reset', + 'ajaxObject', + array( + 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'resend_password_reset_nonce' ), + ) + ); + } + + /** + * Enqueue the password detection page styles. + * + * @return void + */ + public function enqueue_styles(): void { + wp_enqueue_style( + 'password-detection-styles', + plugin_dir_url( __FILE__ ) . 'css/password-detection.css', + array(), + Account_Protection::PACKAGE_VERSION + ); + } + + /** + * Run AJAX request to resend password reset email. + */ + public function ajax_resend_password_reset_email() { + // Verify the nonce for security + check_ajax_referer( 'resend_password_reset_nonce', 'security' ); + + // Check if the user is logged in + if ( ! is_user_logged_in() ) { + wp_send_json_error( array( 'message' => 'User not authenticated' ) ); + } + + $current_user = wp_get_current_user(); + $email = $current_user->user_email; + + // Resend the email + $email_sent = $this->password_reset_email->send( $current_user, $email ); + if ( $email_sent ) { + wp_send_json_success( array( 'message' => 'Resend successful.' ) ); + } else { + wp_send_json_error( array( 'message' => 'Resend failed. ' ) ); + } + } + + /** + * Password validation. + * + * @param string $password The password to validate. + * @return bool True if the password is valid, false otherwise. + */ + public function validate_password( string $password ): bool { + // TODO: Uncomment out once endpoint is live + // Check compromised and common passwords + // $weak_password = self::check_weak_passwords( $password ); + + return $password ? false : true; + } + + /** + * Check if the password is in the list of common/compromised passwords. + * + * @param string $password The password to check. + * @return bool|\WP_Error True if the password is in the list of common/compromised passwords, false otherwise. + */ + public function check_weak_passwords( string $password ) { + $api_url = '/jetpack-protect-weak-password'; + + $is_connected = ( new Connection_Manager() )->is_connected(); + + if ( ! $is_connected ) { + return new \WP_Error( 'site_not_connected' ); + } + + // Hash pass with sha1, and pass first 5 characters to the API + $hashed_password = sha1( $password ); + $password_prefix = substr( $hashed_password, 0, 5 ); + + $response = Client::wpcom_json_api_request_as_blog( + $api_url . '/' . $password_prefix, + '2', + array( 'method' => 'GET' ), + null, + 'wpcom' + ); + + $response_code = wp_remote_retrieve_response_code( $response ); + + if ( is_wp_error( $response ) || 200 !== $response_code || empty( $response['body'] ) ) { + return new \WP_Error( 'failed_fetching_weak_passwords', 'Failed to fetch weak passwords from the server', array( 'status' => $response_code ) ); + } + + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + + // Check if the password is in the list of common/compromised passwords + $password_suffix = substr( $hashed_password, 5 ); + if ( in_array( $password_suffix, $body['compromised'] ?? array(), true ) ) { + return true; + } + + return false; + } + + /** + * Get the password detection usermeta. + * + * @param int $user_id The user ID. + */ + public function get_usermeta( int $user_id ) { + return get_user_meta( $user_id, self::PASSWORD_DETECTION_USER_META_KEY, true ); + } + + /** + * Update the password detection usermeta. + * + * @param int $user_id The user ID. + * @param string $setting The password detection setting. + */ + public function update_usermeta( int $user_id, string $setting ) { + update_user_meta( $user_id, self::PASSWORD_DETECTION_USER_META_KEY, $setting ); + } + + /** + * Delete password detection usermeta for all users. + */ + public function delete_all_usermeta() { + $users = get_users(); + foreach ( $users as $user ) { + $this->delete_usermeta( $user->ID ); + } + } + + /** + * Delete the password detection usermeta. + * + * @param int $user_id The user ID. + */ + public function delete_usermeta( int $user_id ) { + delete_user_meta( $user_id, self::PASSWORD_DETECTION_USER_META_KEY ); + } + + /** + * Delete the password detection usermeta after password reset. + * + * @param \WP_User $user The user object. + */ + public function delete_usermeta_after_password_reset( \WP_User $user ) { + $this->delete_usermeta( $user->ID ); + } + + /** + * Delete the password detection usermeta on profile password update. + * + * @param int $user_id The user ID. + */ + public function delete_usermeta_on_profile_update( int $user_id ) { + if ( + ! empty( $_POST['_wpnonce'] ) && + wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-user_' . $user_id ) + ) { + if ( isset( $_POST['pass1'] ) && ! empty( $_POST['pass1'] ) ) { + $this->delete_usermeta( $user_id ); + } + } + } + + /** + * Render content for password detection page. + * + * @param bool $reset Whether the user is resetting their password. + * @param string $context The context for the password detection page. + * @param string $error The error message to display. + * @param string $masked_email The masked email address. + * @return void + */ + public function render_content( bool $reset, string $context, string $error, string $masked_email ): void { + defined( 'ABSPATH' ) || exit; + ?> + + + + + + <?php echo esc_html( $reset ? 'Jetpack - Stay Secure' : 'Jetpack - Secure Your Account' ); ?> + + + +
+ +

+ +

+ + +

We've encountered an issue verifying your request to proceed without updating your password.

+ +

+ + While attempting to send a verification email to , an error occurred. +

+ + +

Don't worry - To keep your account safe, we've sent a verification email to . After that, we'll guide you through updating your password.

+ +

Please check your inbox and click the link to verify it's you. Alternatively, you can update your password from your account profile.

+

+ Didn't get the email? + Resend email +

+ +

+

It is highly recommended that you update your password.

+
+
+ + +
+
+ + +
+
+

Learn more about the risks of using weak passwords and how to protect your account.

+ +
+ + + + user_login; + // $email = $user->user_email; + + // $key = get_password_reset_key( $user ); + // $locale = get_user_locale( $user ); + // $password_reset_link = network_site_url( 'wp-login.php?login=' . rawurlencode( $username ) . "&key=$key&action=rp", 'login' ) . '&wp_lang=' . $locale; + + // TODO: Update to use custom email method when available, passing $domain_name, $email, $username, and $password_reset_link + return $user ? true : false; + } +} diff --git a/projects/packages/account-protection/src/class-rest-controller.php b/projects/packages/account-protection/src/class-rest-controller.php index d8af3451bc861..e5a45972d1f9e 100644 --- a/projects/packages/account-protection/src/class-rest-controller.php +++ b/projects/packages/account-protection/src/class-rest-controller.php @@ -22,9 +22,9 @@ class REST_Controller { * * @return void */ - public static function register_rest_routes() { + public function register_rest_routes() { // Ensure routes are only initialized once. - static $routes_registered = false; + $routes_registered = false; if ( $routes_registered ) { return; } @@ -34,8 +34,8 @@ public static function register_rest_routes() { '/account-protection', array( 'methods' => WP_REST_Server::READABLE, - 'callback' => __CLASS__ . '::get_settings', - 'permission_callback' => __CLASS__ . '::permissions_callback', + 'callback' => array( $this, 'get_settings' ), + 'permission_callback' => array( $this, 'permissions_callback' ), ) ); @@ -44,8 +44,8 @@ public static function register_rest_routes() { '/account-protection', array( 'methods' => WP_REST_Server::EDITABLE, - 'callback' => __CLASS__ . '::update_settings', - 'permission_callback' => __CLASS__ . '::permissions_callback', + 'callback' => array( $this, 'update_settings' ), + 'permission_callback' => array( $this, 'permissions_callback' ), ) ); @@ -57,8 +57,8 @@ public static function register_rest_routes() { * * @return WP_REST_Response */ - public static function get_settings() { - $settings = Account_Protection::get_settings(); + public function get_settings() { + $settings = ( new Account_Protection() )->get_settings(); return rest_ensure_response( $settings ); } @@ -70,13 +70,13 @@ public static function get_settings() { * * @return WP_REST_Response|WP_Error */ - public static function update_settings( $request ) { + public function update_settings( $request ) { // Strict Mode if ( isset( $request[ Account_Protection::STRICT_MODE_OPTION_NAME ] ) ) { update_option( Account_Protection::STRICT_MODE_OPTION_NAME, $request[ Account_Protection::STRICT_MODE_OPTION_NAME ] ? '1' : '' ); } - return self::get_settings(); + return $this->get_settings(); } /** @@ -84,7 +84,7 @@ public static function update_settings( $request ) { * * @return bool|WP_Error True if user can view the Jetpack admin page. */ - public static function permissions_callback() { + public function permissions_callback() { if ( current_user_can( 'manage_options' ) ) { return true; } diff --git a/projects/packages/account-protection/src/css/password-detection.css b/projects/packages/account-protection/src/css/password-detection.css new file mode 100644 index 0000000000000..d1ec425dd5da3 --- /dev/null +++ b/projects/packages/account-protection/src/css/password-detection.css @@ -0,0 +1,49 @@ +.password-detection-wrapper { + background-color: #f0f0f1; + min-width: 0; + color: #3c434a; + font-family: -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 13px; + line-height: 1.4; +} + +.password-detection { + background: #fff; + width: 420px; + margin: 124px auto; + padding: 26px 24px; + font-weight: 400; + overflow: hidden; + border: 1px solid #c3c4c7; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); +} + +.password-detection-title { + font-size: 24px; + font-weight: 500; +} + +.actions { + display: flex; + flex-direction: column; + gap: 8px; +} + +.action { + height: 36px; + cursor: pointer; + width: 100%; +} + +.action-reset { + margin-top: 10px; + background-color: #0000EE; + border: 1px solid #0000EE; + color: #fff; + } + +.action-proceed { + background-color: #fff; + border: 1px solid #0000EE; + color: #0000EE; +} \ No newline at end of file diff --git a/projects/packages/account-protection/src/js/resend-password-reset.js b/projects/packages/account-protection/src/js/resend-password-reset.js new file mode 100644 index 0000000000000..2e0cdf8a4ab0a --- /dev/null +++ b/projects/packages/account-protection/src/js/resend-password-reset.js @@ -0,0 +1,71 @@ +/* global jQuery, ajaxObject */ +( function ( $ ) { + $( document ).ready( function () { + const attemptLimit = 3; + let attempts = 0; + + $( '#resend-password-reset' ).on( 'click', function ( e ) { + e.preventDefault(); // Prevent the default action + + const message = $( '#resend-password-reset-message' ); + const button = $( this ); + + // Store the original text of the message + const originalMessageText = message.text(); + + // Update message and hide button while resending + message.text( 'Resending email...' ); + button.hide(); + + attempts++; + + // Perform the AJAX request + $.ajax( { + url: ajaxObject.ajax_url, + type: 'POST', + data: { + action: 'resend_password_reset', + security: ajaxObject.nonce, + }, + success: function ( response ) { + if ( response.success ) { + // Show success message + message.text( response.data.message ).show(); + + // Hide the status message and show the button after 5 seconds + setTimeout( function () { + let messageText = originalMessageText; + if ( attempts < attemptLimit ) { + button.show(); + } else { + messageText += 'Please try again later.'; + } + message.text( messageText ).show(); + }, 5000 ); + } else { + // Show error message + let messageText = 'An error occurred. '; + if ( attempts < attemptLimit ) { + button.text( 'Please try again' ).show(); + } else { + messageText += 'Please contact support.'; // TODO: Add support redirect + } + + message.text( messageText ).show(); + } + }, + error: function () { + // Show error message + let messageText = 'An error occurred. '; + if ( attempts < attemptLimit ) { + button.text( 'Please try again' ).show(); + } else { + messageText += 'Please contact support.'; // TODO: Add support redirect + } + + message.text( messageText ).show(); + }, + } ); + } ); + } ); +} )( jQuery ); diff --git a/projects/packages/account-protection/tests/.phpcs.dir.xml b/projects/packages/account-protection/tests/.phpcs.dir.xml deleted file mode 100644 index 46951fe77b37e..0000000000000 --- a/projects/packages/account-protection/tests/.phpcs.dir.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/projects/packages/account-protection/tests/action-test-coverage.sh b/projects/packages/account-protection/tests/action-test-coverage.sh new file mode 100755 index 0000000000000..8a7a1e9de6565 --- /dev/null +++ b/projects/packages/account-protection/tests/action-test-coverage.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -veo pipefail + +EXIT=0 +php -dpcov.directory=. ./vendor/bin/phpunit --coverage-php "$COVERAGE_DIR/integration/php.cov" --configuration tests/php/integration/phpunit.xml.dist || EXIT=1 +php -dpcov.directory=. ./vendor/bin/phpunit --coverage-php "$COVERAGE_DIR/unit/php.cov" --configuration tests/php/unit/phpunit.xml.dist || EXIT=1 + +exit $EXIT diff --git a/projects/packages/account-protection/tests/php/integration/bootstrap.php b/projects/packages/account-protection/tests/php/integration/bootstrap.php new file mode 100644 index 0000000000000..4c1205f352a1d --- /dev/null +++ b/projects/packages/account-protection/tests/php/integration/bootstrap.php @@ -0,0 +1,16 @@ + + + + + + + ../../../src + + + + + . + + + diff --git a/projects/packages/account-protection/tests/php/bootstrap.php b/projects/packages/account-protection/tests/php/unit/bootstrap.php similarity index 64% rename from projects/packages/account-protection/tests/php/bootstrap.php rename to projects/packages/account-protection/tests/php/unit/bootstrap.php index 46763b04a2cdb..e16bad0ecf0bf 100644 --- a/projects/packages/account-protection/tests/php/bootstrap.php +++ b/projects/packages/account-protection/tests/php/unit/bootstrap.php @@ -8,4 +8,4 @@ /** * Include the composer autoloader. */ -require_once __DIR__ . '/../../vendor/autoload.php'; +require_once __DIR__ . '/../../../vendor/autoload.php'; diff --git a/projects/packages/account-protection/tests/php/unit/phpunit.xml.dist b/projects/packages/account-protection/tests/php/unit/phpunit.xml.dist new file mode 100644 index 0000000000000..e901dd1acf030 --- /dev/null +++ b/projects/packages/account-protection/tests/php/unit/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + + + ../../../src + + + + + . + + + diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index b84d338782098..554570f666289 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -15,4 +15,4 @@ use Automattic\Jetpack\Account_Protection\Account_Protection; -Account_Protection::init(); +( new Account_Protection() )->init(); diff --git a/projects/plugins/protect/src/class-jetpack-protect.php b/projects/plugins/protect/src/class-jetpack-protect.php index 8c0ec4f1b5b99..fe13bd83a670e 100644 --- a/projects/plugins/protect/src/class-jetpack-protect.php +++ b/projects/plugins/protect/src/class-jetpack-protect.php @@ -138,7 +138,7 @@ public function init() { REST_Controller::init(); My_Jetpack_Initializer::init(); Site_Health::init(); - Account_Protection::init(); + ( new Account_Protection() )->init(); // Sets up JITMS. JITM::configure(); @@ -214,6 +214,7 @@ public function initial_state() { // phpcs:disable WordPress.Security.NonceVerification.Recommended $refresh_status_from_wpcom = isset( $_GET['checkPlan'] ); $status = Status::get_status( $refresh_status_from_wpcom ); + $account_protection = new Account_Protection(); $initial_state = array( 'apiRoot' => esc_url_raw( rest_url() ), @@ -233,8 +234,8 @@ public function initial_state() { 'hasPlan' => Plan::has_required_plan(), 'onboardingProgress' => Onboarding::get_current_user_progress(), 'accountProtection' => array( - 'isEnabled' => Account_Protection::is_enabled(), - 'settings' => Account_Protection::get_settings(), + 'isEnabled' => $account_protection->is_enabled(), + 'settings' => $account_protection->get_settings(), ), 'waf' => array( 'wafSupported' => Waf_Runner::is_supported_environment(), diff --git a/projects/plugins/protect/src/class-rest-controller.php b/projects/plugins/protect/src/class-rest-controller.php index 32d85f5e8ad97..b6ddb432afa23 100644 --- a/projects/plugins/protect/src/class-rest-controller.php +++ b/projects/plugins/protect/src/class-rest-controller.php @@ -371,8 +371,9 @@ public static function api_scan() { * @return WP_REST_Response|WP_Error */ public static function api_toggle_account_protection() { - if ( Account_Protection::is_enabled() ) { - $disabled = Account_Protection::disable(); + $account_protection = new Account_Protection(); + if ( $account_protection->is_enabled() ) { + $disabled = $account_protection->disable(); if ( ! $disabled ) { return new WP_Error( 'account_protection_disable_failed', @@ -384,7 +385,7 @@ public static function api_toggle_account_protection() { return rest_ensure_response( true ); } - $enabled = Account_Protection::enable(); + $enabled = $account_protection->enable(); if ( ! $enabled ) { return new WP_Error( 'account_protection_enable_failed', @@ -402,7 +403,7 @@ public static function api_toggle_account_protection() { * @return WP_Rest_Response */ public static function api_get_account_protection() { - return new WP_REST_Response( Account_Protection::is_enabled() ); + return new WP_REST_Response( ( new Account_Protection() )->get_settings() ); } /**