Skip to content

Commit

Permalink
Account Protection: Add password detection flow (#41105)
Browse files Browse the repository at this point in the history
* Add Account Protection toggle to Jetpack security settings

* Import package and run activation/deactivation on module toggle

* changelog

* Add Protect Settings page and hook up Account Protection toggle

* changelog

* Update changelog

* Register modules on plugin activation

* Ensure package is initialized on plugin activation

* Make account protection class init static

* Add auth hooks, redirect and a custom login action template

* Reorg, add Password_Detection class

* Remove user cxn req and banner

* Do not enabled module by default

* Add strict mode option and settings toggle

* changelog

* Add strict mode toggle

* Add strict mode toggle and endpoints

* Reorg and add kill switch and is supported check

* Add testing infrastructure

* Add email handlings, resend AJAX action, and attempt limitations

* Add nonces, checks and template error handling

* Use method over template to avoid lint errors

* Improve render_password_detection_template, update SVG file ext

* Remove template file and include

* Prep for validation endpoints

* Update classes to be dynamic

* Add constructors

* Reorg user meta methods

* Add type declarations and hinting

* Simplify method naming

* Use dynamic classes

* Update class dependencies

* Fix copy

* Revert unrelated changes

* Revert unrelated changes

* Fix method calls

* Do not activate by default

* Fix phan errors

* Changelog

* Update composer deps

* Update lock files, add constructor method

* Fix php warning

* Update lock file

* Changelog

* Fix Password_Detection constructor

* Changelog

* More changelogs

* Remove comments

* Fix static analysis errors

* Remove top level phpunit.xml.dist

* Remove never return type

* Revert tests dir changes in favour of a dedicated task

* Add tests dir

* Reapply default test infrastructure

* Reorg and rename

* Update @Package

* Use never phpdoc return type as per static analysis error

* Enable module by default

* Enable module by default

* Update projects/plugins/protect/src/js/data/account-protection/use-account-protection-mutation.ts

Co-authored-by: Kolja Zuelsdorf <[email protected]>

* Update lock files

---------

Co-authored-by: Kolja Zuelsdorf <[email protected]>
  • Loading branch information
dkmyta and ArSn authored Jan 24, 2025
1 parent 1ecf4df commit 1ec2c34
Show file tree
Hide file tree
Showing 9 changed files with 690 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adds the password detection flow
21 changes: 21 additions & 0 deletions projects/packages/account-protection/src/assets/jetpack-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
119 changes: 107 additions & 12 deletions projects/packages/account-protection/src/class-account-protection.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* Class Account_Protection
*/
class Account_Protection {

const PACKAGE_VERSION = '0.1.0-alpha';
const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection';
const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode';
Expand All @@ -25,39 +24,83 @@ class Account_Protection {
*/
private $modules;

/**
* Password detection instance.
*
* @var Password_Detection
*/
private $password_detection;

/**
* Account_Protection constructor.
*
* @param ?Modules $modules Modules instance.
* @param ?Modules $modules Modules instance.
* @param ?Password_Detection $password_detection Password detection instance.
*/
public function __construct( ?Modules $modules = null ) {
$this->modules = $modules ?? new Modules();
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 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_' . 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 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 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();
}

/**
Expand Down Expand Up @@ -87,20 +130,72 @@ public function enable() {
*
* @return bool
*/
public function disable() {
public function disable(): bool {
// Return true if already disabled.
if ( ! $this->is_enabled() ) {
return true;
}
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;
}

/**
* Get the account protection settings.
*
* @return array
*/
public function get_settings() {
public function get_settings(): array {
$settings = array(
self::STRICT_MODE_OPTION_NAME => get_option( self::STRICT_MODE_OPTION_NAME, false ),
);
Expand Down
Loading

0 comments on commit 1ec2c34

Please sign in to comment.