From 3f56ee70659d1bf8af8b9d259826c2a3483c521f Mon Sep 17 00:00:00 2001 From: dkmyta Date: Thu, 9 Jan 2025 10:10:26 -0800 Subject: [PATCH 01/20] Add Account Protection toggle to Jetpack security settings --- pnpm-lock.yaml | 2 + .../client/components/settings-card/index.jsx | 19 +++++++ .../_inc/client/lib/plans/constants.js | 2 + .../client/security/account-protection.jsx | 50 +++++++++++++++++++ .../jetpack/_inc/client/security/index.jsx | 4 ++ .../jetpack/modules/account-protection.php | 14 ++++++ 6 files changed, 91 insertions(+) create mode 100644 projects/plugins/jetpack/_inc/client/security/account-protection.jsx create mode 100644 projects/plugins/jetpack/modules/account-protection.php diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 381294a9066fb..2dc54f62fb891 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1765,6 +1765,8 @@ importers: specifier: 4.9.1 version: 4.9.1(webpack@5.94.0) + projects/packages/account-protection: {} + projects/packages/admin-ui: {} projects/packages/assets: diff --git a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx index 8cb3d077921bd..6dd2932eb7285 100644 --- a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx +++ b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx @@ -24,6 +24,7 @@ import { getJetpackProductUpsellByFeature, FEATURE_JETPACK_BLAZE, FEATURE_JETPACK_EARN, + FEATURE_JETPACK_ACCOUNT_PROTECTION, } from 'lib/plans/constants'; import ProStatus from 'pro-status'; import { @@ -455,6 +456,24 @@ export const SettingsCard = inprops => { rna /> ); + case FEATURE_JETPACK_ACCOUNT_PROTECTION: + if ( props.hasConnectedOwner ) { + return ''; + } + + return ( + + ); default: return ''; } diff --git a/projects/plugins/jetpack/_inc/client/lib/plans/constants.js b/projects/plugins/jetpack/_inc/client/lib/plans/constants.js index 0a486259173e5..12a0743eb48cc 100644 --- a/projects/plugins/jetpack/_inc/client/lib/plans/constants.js +++ b/projects/plugins/jetpack/_inc/client/lib/plans/constants.js @@ -417,6 +417,7 @@ export const FEATURE_POST_BY_EMAIL = 'post-by-email-jetpack'; export const FEATURE_JETPACK_SOCIAL = 'social-jetpack'; export const FEATURE_JETPACK_BLAZE = 'blaze-jetpack'; export const FEATURE_JETPACK_EARN = 'earn-jetpack'; +export const FEATURE_JETPACK_ACCOUNT_PROTECTION = 'account-protection-jetpack'; // Upsells export const JETPACK_FEATURE_PRODUCT_UPSELL_MAP = { @@ -439,6 +440,7 @@ export const JETPACK_FEATURE_PRODUCT_UPSELL_MAP = { [ FEATURE_VIDEOPRESS ]: PLAN_JETPACK_VIDEOPRESS, [ FEATURE_NEWSLETTER_JETPACK ]: PLAN_JETPACK_CREATOR_YEARLY, [ FEATURE_WORDADS_JETPACK ]: PLAN_JETPACK_SECURITY_T1_YEARLY, + [ FEATURE_JETPACK_ACCOUNT_PROTECTION ]: PLAN_JETPACK_FREE, }; /** diff --git a/projects/plugins/jetpack/_inc/client/security/account-protection.jsx b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx new file mode 100644 index 0000000000000..39b074365c852 --- /dev/null +++ b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx @@ -0,0 +1,50 @@ +import { getRedirectUrl } from '@automattic/jetpack-components'; +import { __, _x } from '@wordpress/i18n'; +import React, { Component } from 'react'; +import { withModuleSettingsFormHelpers } from 'components/module-settings/with-module-settings-form-helpers'; +import { ModuleToggle } from 'components/module-toggle'; +import SettingsCard from 'components/settings-card'; +import SettingsGroup from 'components/settings-group'; +import { FEATURE_JETPACK_ACCOUNT_PROTECTION } from '../lib/plans/constants'; + +const AccountProtectionComponent = class extends Component { + render() { + const isAccountProtectionActive = this.props.getOptionValue( 'account-protection' ), + unavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'account-protection' ); + return ( + + + + + { __( 'Require strong passwords', 'jetpack' ) } + + + + + ); + } +}; + +export const AccountProtection = withModuleSettingsFormHelpers( AccountProtectionComponent ); diff --git a/projects/plugins/jetpack/_inc/client/security/index.jsx b/projects/plugins/jetpack/_inc/client/security/index.jsx index f6e2c9369fc53..ff1ec0efad4f2 100644 --- a/projects/plugins/jetpack/_inc/client/security/index.jsx +++ b/projects/plugins/jetpack/_inc/client/security/index.jsx @@ -12,6 +12,7 @@ import { isModuleFound } from 'state/search'; import { getSettings } from 'state/settings'; import { siteHasFeature } from 'state/site'; import { isPluginActive, isPluginInstalled } from 'state/site/plugins'; +import { AccountProtection } from './account-protection'; import AllowList from './allowList'; import Antispam from './antispam'; import BackupsScan from './backups-scan'; @@ -91,6 +92,8 @@ export class Security extends Component { ); + const foundAccountProtection = this.props.isModuleFound( 'account-protection' ); + return (
@@ -112,6 +115,7 @@ export class Security extends Component { ) } + { foundAccountProtection && } { foundWaf && } { foundProtect && } { ( foundWaf || foundProtect ) && } diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php new file mode 100644 index 0000000000000..87bd3c9925d1d --- /dev/null +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -0,0 +1,14 @@ + Date: Thu, 9 Jan 2025 11:43:59 -0800 Subject: [PATCH 02/20] Import package and run activation/deactivation on module toggle --- .../src/class-account-protection.php | 66 ++++++++- projects/plugins/jetpack/composer.json | 1 + projects/plugins/jetpack/composer.lock | 139 +++++++++++++----- .../jetpack/modules/account-protection.php | 5 + 4 files changed, 174 insertions(+), 37 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index c895dc25a0216..29623cfd361f1 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -1,16 +1,76 @@ is_active( 'account-protection' ); + } + + /** + * Enables the account protection module. + * + * @return bool + */ + public static function enable() { + // Return true if already enabled. + if ( self::is_enabled() ) { + return true; + } + return ( new Modules() )->activate( 'account-protection', false, false ); + } + + /** + * Disables the account protection module. + * + * @return bool + */ + public static function disable() { + // Return true if already disabled. + if ( ! self::is_enabled() ) { + return true; + } + return ( new Modules() )->deactivate( 'account-protection' ); + } } diff --git a/projects/plugins/jetpack/composer.json b/projects/plugins/jetpack/composer.json index bd613184927c5..ee311a946c40e 100644 --- a/projects/plugins/jetpack/composer.json +++ b/projects/plugins/jetpack/composer.json @@ -12,6 +12,7 @@ "ext-json": "*", "ext-openssl": "*", "automattic/jetpack-a8c-mc-stats": "@dev", + "automattic/jetpack-account-protection": "@dev", "automattic/jetpack-admin-ui": "@dev", "automattic/jetpack-assets": "@dev", "automattic/jetpack-autoloader": "@dev", diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index f823169790931..2c890a56ac012 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7b6c53f88fcb9c7098d80137fd6d13c1", + "content-hash": "cbb88a4e4e1b0088ff12393af82e5cdc", "packages": [ { "name": "automattic/jetpack-a8c-mc-stats", @@ -59,6 +59,76 @@ "relative": true } }, + { + "name": "automattic/jetpack-account-protection", + "version": "dev-trunk", + "dist": { + "type": "path", + "url": "../../packages/account-protection", + "reference": "c22829e6a80ff9f5cd10e4b4eece3d405f69e8f9" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "automattic/jetpack-changelogger": "@dev", + "automattic/wordbless": "dev-master", + "yoast/phpunit-polyfills": "^1.1.1" + }, + "suggest": { + "automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package." + }, + "type": "jetpack-library", + "extra": { + "autotagger": true, + "branch-alias": { + "dev-trunk": "0.1.x-dev" + }, + "changelogger": { + "link-template": "https://github.com/Automattic/jetpack-account-protection/compare/v${old}...v${new}" + }, + "mirror-repo": "Automattic/jetpack-account-protection", + "textdomain": "jetpack-account-protection", + "version-constants": { + "::PACKAGE_VERSION": "src/class-account-protection.php" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "scripts": { + "build-development": [ + "echo 'Add your build step to composer.json, please!'" + ], + "build-production": [ + "echo 'Add your build step to composer.json, please!'" + ], + "phpunit": [ + "./vendor/phpunit/phpunit/phpunit --colors=always" + ], + "post-install-cmd": [ + "WorDBless\\Composer\\InstallDropin::copy" + ], + "post-update-cmd": [ + "WorDBless\\Composer\\InstallDropin::copy" + ], + "test-coverage": [ + "php -dpcov.directory=. ./vendor/bin/phpunit --coverage-php \"$COVERAGE_DIR/php.cov\"" + ], + "test-php": [ + "@composer phpunit" + ] + }, + "license": [ + "GPL-2.0-or-later" + ], + "description": "Account protection", + "transport-options": { + "relative": true + } + }, { "name": "automattic/jetpack-admin-ui", "version": "dev-trunk", @@ -3320,16 +3390,16 @@ "packages-dev": [ { "name": "antecedent/patchwork", - "version": "2.2.0", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "b07d4fb37c3c723c8755122160c089e077d5de65" + "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/b07d4fb37c3c723c8755122160c089e077d5de65", - "reference": "b07d4fb37c3c723c8755122160c089e077d5de65", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/1bf183a3e1bd094f231a2128b9ecc5363c269245", + "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245", "shasum": "" }, "require": { @@ -3362,9 +3432,9 @@ ], "support": { "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.2.0" + "source": "https://github.com/antecedent/patchwork/tree/2.2.1" }, - "time": "2024-09-27T16:59:55+00:00" + "time": "2024-12-11T10:19:54+00:00" }, { "name": "automattic/jetpack-changelogger", @@ -3686,16 +3756,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -3738,9 +3808,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -5300,16 +5370,16 @@ }, { "name": "symfony/console", - "version": "v7.2.0", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", "shasum": "" }, "require": { @@ -5373,7 +5443,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.0" + "source": "https://github.com/symfony/console/tree/v7.2.1" }, "funding": [ { @@ -5389,7 +5459,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2024-12-11T03:49:26+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5410,12 +5480,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5722,8 +5792,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5861,12 +5931,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -6059,16 +6129,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "e9c8413de4c8ae03d2923a44f17d0d7dad1b96be" + "reference": "0b31ce834facf03b8b44b6587e65b3cf1d7cfb94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/e9c8413de4c8ae03d2923a44f17d0d7dad1b96be", - "reference": "e9c8413de4c8ae03d2923a44f17d0d7dad1b96be", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/0b31ce834facf03b8b44b6587e65b3cf1d7cfb94", + "reference": "0b31ce834facf03b8b44b6587e65b3cf1d7cfb94", "shasum": "" }, "require": { @@ -6118,13 +6188,14 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2024-09-06T22:03:10+00:00" + "time": "2025-01-08T16:58:34+00:00" } ], "aliases": [], "minimum-stability": "dev", "stability-flags": { "automattic/jetpack-a8c-mc-stats": 20, + "automattic/jetpack-account-protection": 20, "automattic/jetpack-admin-ui": 20, "automattic/jetpack-assets": 20, "automattic/jetpack-autoloader": 20, diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index 87bd3c9925d1d..76849893538ea 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -12,3 +12,8 @@ * * @package automattic/jetpack */ + +use Automattic\Jetpack\Account_Protection\Account_Protection; + +$account_protection = new Account_Protection(); +$account_protection->init(); From c83c60454e2ae906f03265ad927bceb036f47c38 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Thu, 9 Jan 2025 11:44:55 -0800 Subject: [PATCH 03/20] changelog --- .../add-jetpack-account-protection-security-settings | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings diff --git a/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings b/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings new file mode 100644 index 0000000000000..04ebecc39c6e1 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Adds a Account Protection module toggle From b64fdaf43b2318dc121e47577a01ad21afbd147d Mon Sep 17 00:00:00 2001 From: dkmyta Date: Thu, 9 Jan 2025 13:48:06 -0800 Subject: [PATCH 04/20] Update changelog --- .../changelog/add-jetpack-account-protection-security-settings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings b/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings index 04ebecc39c6e1..4c36bca9e49ec 100644 --- a/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings +++ b/projects/plugins/jetpack/changelog/add-jetpack-account-protection-security-settings @@ -1,4 +1,4 @@ Significance: minor Type: enhancement -Adds a Account Protection module toggle +Adds the Account Protection module toggle From 3f90fe40747ccdcaccc8294fbac5163ebd3ec768 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Thu, 9 Jan 2025 15:09:17 -0800 Subject: [PATCH 05/20] Make account protection class init static --- .../account-protection/src/class-account-protection.php | 2 +- projects/plugins/jetpack/modules/account-protection.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index 29623cfd361f1..c623faa14781a 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -19,7 +19,7 @@ class Account_Protection { /** * Initializes the configurations needed for the account protection module. */ - public function init() { + public static function init() { // 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' ); diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index 76849893538ea..891d023f7fe37 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -15,5 +15,4 @@ use Automattic\Jetpack\Account_Protection\Account_Protection; -$account_protection = new Account_Protection(); -$account_protection->init(); +Account_Protection::init(); From 7eabdd3f5851b1231271c2d5967f3a987679b1d3 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Tue, 14 Jan 2025 12:50:43 -0800 Subject: [PATCH 06/20] Remove user cxn req and banner --- .../client/components/settings-card/index.jsx | 19 ------------------- .../jetpack/modules/account-protection.php | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx index 6dd2932eb7285..8cb3d077921bd 100644 --- a/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx +++ b/projects/plugins/jetpack/_inc/client/components/settings-card/index.jsx @@ -24,7 +24,6 @@ import { getJetpackProductUpsellByFeature, FEATURE_JETPACK_BLAZE, FEATURE_JETPACK_EARN, - FEATURE_JETPACK_ACCOUNT_PROTECTION, } from 'lib/plans/constants'; import ProStatus from 'pro-status'; import { @@ -456,24 +455,6 @@ export const SettingsCard = inprops => { rna /> ); - case FEATURE_JETPACK_ACCOUNT_PROTECTION: - if ( props.hasConnectedOwner ) { - return ''; - } - - return ( - - ); default: return ''; } diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index 891d023f7fe37..4814ac0eca89b 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -5,7 +5,7 @@ * Sort Order: 4 * First Introduced: 14.3 * Requires Connection: Yes - * Requires User Connection: Yes + * Requires User Connection: No * Auto Activate: Yes * Module Tags: Account Protection * Feature: Security From 5a1af0b530159883ee67c31c8bfe6cc9cbfb5d8c Mon Sep 17 00:00:00 2001 From: dkmyta Date: Tue, 14 Jan 2025 12:59:34 -0800 Subject: [PATCH 07/20] Do not enabled module by default --- projects/plugins/jetpack/modules/account-protection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index 4814ac0eca89b..b84d338782098 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -6,7 +6,7 @@ * First Introduced: 14.3 * Requires Connection: Yes * Requires User Connection: No - * Auto Activate: Yes + * Auto Activate: No * Module Tags: Account Protection * Feature: Security * From 3b35efe33d914f06f2596a5c4bfd26e339380a50 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Wed, 15 Jan 2025 06:23:55 -0800 Subject: [PATCH 08/20] Add strict mode option and settings toggle --- projects/js-packages/api/index.jsx | 10 ++ .../src/class-account-protection.php | 7 +- .../src/class-rest-controller.php | 100 ++++++++++++ .../index.jsx | 44 +++++ .../client/security/account-protection.jsx | 151 +++++++++++++++++- .../_inc/client/security/allowList.jsx | 2 +- .../jetpack/_inc/client/security/index.jsx | 2 +- .../jetpack/_inc/client/security/style.scss | 24 ++- .../state/account-protection/actions.js | 66 ++++++++ .../client/state/account-protection/index.js | 2 + .../state/account-protection/reducer.js | 87 ++++++++++ .../jetpack/_inc/client/state/action-types.js | 9 ++ .../jetpack/_inc/client/state/reducer.js | 2 + .../lib/class.core-rest-api-endpoints.php | 9 +- ....wpcom-json-api-site-settings-endpoint.php | 2 + projects/plugins/jetpack/modules/waf.php | 2 +- 16 files changed, 508 insertions(+), 11 deletions(-) create mode 100644 projects/packages/account-protection/src/class-rest-controller.php create mode 100644 projects/plugins/jetpack/_inc/client/components/data/query-account-protection-settings/index.jsx create mode 100644 projects/plugins/jetpack/_inc/client/state/account-protection/actions.js create mode 100644 projects/plugins/jetpack/_inc/client/state/account-protection/index.js create mode 100644 projects/plugins/jetpack/_inc/client/state/account-protection/reducer.js diff --git a/projects/js-packages/api/index.jsx b/projects/js-packages/api/index.jsx index 8233d0ba8a616..6f6cdffe0b325 100644 --- a/projects/js-packages/api/index.jsx +++ b/projects/js-packages/api/index.jsx @@ -510,6 +510,16 @@ function JetpackRestApiClient( root, nonce ) { getRequest( `${ wpcomOriginApiUrl }jetpack/v4/search/stats`, getParams ) .then( checkStatus ) .then( parseJsonResponse ), + fetchAccountProtectionSettings: () => + getRequest( `${ apiRoot }jetpack/v4/account-protection`, getParams ) + .then( checkStatus ) + .then( parseJsonResponse ), + updateAccountProtectionSettings: newSettings => + postRequest( `${ apiRoot }jetpack/v4/account-protection`, postParams, { + body: JSON.stringify( newSettings ), + } ) + .then( checkStatus ) + .then( parseJsonResponse ), fetchWafSettings: () => getRequest( `${ apiRoot }jetpack/v4/waf`, getParams ) .then( checkStatus ) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index c623faa14781a..f809bbe567657 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -14,7 +14,9 @@ */ class Account_Protection { - const PACKAGE_VERSION = '1.0.0-alpha'; + const PACKAGE_VERSION = '1.0.0-alpha'; + const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection'; + const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode'; /** * Initializes the configurations needed for the account protection module. @@ -23,6 +25,9 @@ public static function init() { // 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' ); + + // Register REST routes + add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) ); } /** diff --git a/projects/packages/account-protection/src/class-rest-controller.php b/projects/packages/account-protection/src/class-rest-controller.php new file mode 100644 index 0000000000000..60e6f71f6cc74 --- /dev/null +++ b/projects/packages/account-protection/src/class-rest-controller.php @@ -0,0 +1,100 @@ + WP_REST_Server::READABLE, + 'callback' => __CLASS__ . '::get_settings', + 'permission_callback' => __CLASS__ . '::permissions_callback', + ) + ); + + register_rest_route( + 'jetpack/v4', + '/account-protection', + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => __CLASS__ . '::update_settings', + 'permission_callback' => __CLASS__ . '::permissions_callback', + ) + ); + + $routes_registered = true; + } + + /** + * Account Protection Settings Endpoint + * + * @return WP_REST_Response + */ + public static function get_settings() { + return rest_ensure_response( + array( + Account_Protection::STRICT_MODE_OPTION_NAME => get_option( Account_Protection::STRICT_MODE_OPTION_NAME ), + ) + ); + } + + /** + * Update Account Protection Settings Endpoint + * + * @param WP_REST_Request $request The API request. + * + * @return WP_REST_Response|WP_Error + */ + public static 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(); + } + + /** + * Account Protection Endpoint Permissions Callback + * + * @return bool|WP_Error True if user can view the Jetpack admin page. + */ + public static function permissions_callback() { + if ( current_user_can( 'manage_options' ) ) { + return true; + } + + return new WP_Error( + 'invalid_user_permission_manage_options', + REST_Connector::get_user_permissions_error_msg(), + array( 'status' => rest_authorization_required_code() ) + ); + } +} diff --git a/projects/plugins/jetpack/_inc/client/components/data/query-account-protection-settings/index.jsx b/projects/plugins/jetpack/_inc/client/components/data/query-account-protection-settings/index.jsx new file mode 100644 index 0000000000000..d86ec79e0917b --- /dev/null +++ b/projects/plugins/jetpack/_inc/client/components/data/query-account-protection-settings/index.jsx @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types'; +import { Component } from 'react'; +import { connect } from 'react-redux'; +import { + fetchAccountProtectionSettings, + isFetchingAccountProtectionSettings, +} from 'state/account-protection'; +import { isOfflineMode } from 'state/connection'; + +class QueryAccountProtectionSettings extends Component { + static propTypes = { + isFetchingAccountProtectionSettings: PropTypes.bool, + isOfflineMode: PropTypes.bool, + }; + + static defaultProps = { + isFetchingAccountProtectionSettings: false, + isOfflineMode: false, + }; + + componentDidMount() { + if ( ! this.props.isFetchingAccountProtectionSettings && ! this.props.isOfflineMode ) { + this.props.fetchAccountProtectionSettings(); + } + } + + render() { + return null; + } +} + +export default connect( + state => { + return { + isFetchingAccountProtectionSettings: isFetchingAccountProtectionSettings( state ), + isOfflineMode: isOfflineMode( state ), + }; + }, + dispatch => { + return { + fetchAccountProtectionSettings: () => dispatch( fetchAccountProtectionSettings() ), + }; + } +)( QueryAccountProtectionSettings ); diff --git a/projects/plugins/jetpack/_inc/client/security/account-protection.jsx b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx index 39b074365c852..6b5e9126b7493 100644 --- a/projects/plugins/jetpack/_inc/client/security/account-protection.jsx +++ b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx @@ -1,16 +1,99 @@ -import { getRedirectUrl } from '@automattic/jetpack-components'; +import { ToggleControl } from '@automattic/jetpack-components'; +import { ExternalLink } from '@wordpress/components'; +import { createInterpolateElement } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { FormFieldset } from 'components/forms'; +import { createNotice, removeNotice } from 'components/global-notices/state/notices/actions'; import { withModuleSettingsFormHelpers } from 'components/module-settings/with-module-settings-form-helpers'; import { ModuleToggle } from 'components/module-toggle'; import SettingsCard from 'components/settings-card'; import SettingsGroup from 'components/settings-group'; +import QueryAccountProtectionSettings from '../components/data/query-account-protection-settings'; +import InfoPopover from '../components/info-popover'; import { FEATURE_JETPACK_ACCOUNT_PROTECTION } from '../lib/plans/constants'; +import { updateAccountProtectionSettings } from '../state/account-protection/actions'; +import { + getAccountProtectionSettings, + isFetchingAccountProtectionSettings, + isUpdatingAccountProtectionSettings, +} from '../state/account-protection/reducer'; + +const AccountProtection = class extends Component { + /** + * Get options for initial state. + * + * @return {object} + */ + state = { + strictMode: this.props.settings?.strictMode, + }; + + /** + * Keep the form values in sync with updates to the settings prop. + * + * @param {object} prevProps - Next render props. + */ + componentDidUpdate = prevProps => { + // Sync the form values with the settings prop. + if ( this.props.settings !== prevProps.settings ) { + this.setState( { + ...this.state, + strictMode: this.props.settings?.strictMode, + } ); + } + }; + + /** + * Handle settings updates. + * + * @return {void} + */ + onSubmit = () => { + this.props.removeNotice( 'module-setting-update' ); + this.props.removeNotice( 'module-setting-update-success' ); + + this.props.createNotice( 'is-info', __( 'Updating settingsā€¦', 'jetpack' ), { + id: 'module-setting-update', + } ); + this.props + .updateAccountProtectionSettings( this.state ) + .then( () => { + this.props.removeNotice( 'module-setting-update' ); + this.props.createNotice( 'is-success', __( 'Updated Settings.', 'jetpack' ), { + id: 'module-setting-update-success', + } ); + } ) + .catch( () => { + this.props.removeNotice( 'module-setting-update' ); + this.props.createNotice( 'is-error', __( 'Error updating settings.', 'jetpack' ), { + id: 'module-setting-update', + } ); + } ); + }; + + /** + * Toggle strict mode. + */ + toggleStrictMode = () => { + const state = { + ...this.state, + strictMode: ! this.state.strictMode, + }; + + this.setState( state, this.onSubmit ); + }; -const AccountProtectionComponent = class extends Component { render() { const isAccountProtectionActive = this.props.getOptionValue( 'account-protection' ), unavailableInOfflineMode = this.props.isUnavailableInOfflineMode( 'account-protection' ); + const baseInputDisabledCase = + ! isAccountProtectionActive || + unavailableInOfflineMode || + this.props.isFetchingAccountProtectionSettings || + this.props.isSavingAnyOption( [ 'account-protection' ] ); + return ( + { isAccountProtectionActive && } - { __( 'Require strong passwords', 'jetpack' ) } + { __( + 'Protect your site with enhanced password detection and profile management security.', + 'jetpack' + ) } + { isAccountProtectionActive && ( + +
+ + + { __( 'Reqiure strong passwords', 'jetpack' ) } + + + { createInterpolateElement( + __( + 'Allow Jetpack to enforce strict password rules. Learn more
Privacy Information', + 'jetpack' + ), + { + ExternalLink: , // TODO: Update this redirect URL + hr:
, + } + ) } +
+
+ } + /> +
+ + ) } ); } }; -export const AccountProtection = withModuleSettingsFormHelpers( AccountProtectionComponent ); +export default connect( + state => { + return { + isFetchingSettings: isFetchingAccountProtectionSettings( state ), + isUpdatingAccountProtectionSettings: isUpdatingAccountProtectionSettings( state ), + settings: getAccountProtectionSettings( state ), + }; + }, + dispatch => { + return { + updateAccountProtectionSettings: newSettings => + dispatch( updateAccountProtectionSettings( newSettings ) ), + createNotice: ( type, message, props ) => dispatch( createNotice( type, message, props ) ), + removeNotice: notice => dispatch( removeNotice( notice ) ), + }; + } +)( withModuleSettingsFormHelpers( AccountProtection ) ); diff --git a/projects/plugins/jetpack/_inc/client/security/allowList.jsx b/projects/plugins/jetpack/_inc/client/security/allowList.jsx index e102a89cd8918..8f9d8621477ab 100644 --- a/projects/plugins/jetpack/_inc/client/security/allowList.jsx +++ b/projects/plugins/jetpack/_inc/client/security/allowList.jsx @@ -155,7 +155,7 @@ const AllowList = class extends Component { label={ { __( - "Prevent Jetpack's security features from blocking specific IP addresses", + "Prevent Jetpack's security features from blocking specific IP addresses.", 'jetpack' ) } diff --git a/projects/plugins/jetpack/_inc/client/security/index.jsx b/projects/plugins/jetpack/_inc/client/security/index.jsx index ff1ec0efad4f2..d4677461de9ea 100644 --- a/projects/plugins/jetpack/_inc/client/security/index.jsx +++ b/projects/plugins/jetpack/_inc/client/security/index.jsx @@ -12,7 +12,7 @@ import { isModuleFound } from 'state/search'; import { getSettings } from 'state/settings'; import { siteHasFeature } from 'state/site'; import { isPluginActive, isPluginInstalled } from 'state/site/plugins'; -import { AccountProtection } from './account-protection'; +import AccountProtection from './account-protection'; import AllowList from './allowList'; import Antispam from './antispam'; import BackupsScan from './backups-scan'; diff --git a/projects/plugins/jetpack/_inc/client/security/style.scss b/projects/plugins/jetpack/_inc/client/security/style.scss index 9a4608ee3bf57..385e7feaa710f 100644 --- a/projects/plugins/jetpack/_inc/client/security/style.scss +++ b/projects/plugins/jetpack/_inc/client/security/style.scss @@ -56,7 +56,9 @@ } &__share-data-popover { - margin-left: 8px; + display: flex; + align-items: center; + margin-left: 4px; } &__upgrade-popover { @@ -189,4 +191,24 @@ .jp-form-settings-group p { margin-bottom: 0.5rem; +} + +.account-protection__settings { + &__toggle-setting { + flex-wrap: wrap; + display: flex; + margin-bottom: 24px; + + &__label { + display: flex; + align-items: center; + } + } + + &__strict-mode-popover { + display: flex; + align-items: center; + margin-left: 4px; + } + } \ No newline at end of file diff --git a/projects/plugins/jetpack/_inc/client/state/account-protection/actions.js b/projects/plugins/jetpack/_inc/client/state/account-protection/actions.js new file mode 100644 index 0000000000000..feee531d78a38 --- /dev/null +++ b/projects/plugins/jetpack/_inc/client/state/account-protection/actions.js @@ -0,0 +1,66 @@ +import restApi from '@automattic/jetpack-api'; +import { + ACCOUNT_PROTECTION_SETTINGS_FETCH, + ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE, + ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL, + ACCOUNT_PROTECTION_SETTINGS_UPDATE, + ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS, + ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL, +} from 'state/action-types'; + +export const fetchAccountProtectionSettings = () => { + return dispatch => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_FETCH, + } ); + return restApi + .fetchAccountProtectionSettings() + .then( settings => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE, + settings, + } ); + return settings; + } ) + .catch( error => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL, + error: error, + } ); + } ); + }; +}; + +/** + * Update Account Protection Settings + * + * @param {object} newSettings - The new settings to be saved. + * @param {boolean} newSettings.strictMode - Whether strict mode is enabled. + * @return {Function} - The action. + */ +export const updateAccountProtectionSettings = newSettings => { + return dispatch => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_UPDATE, + } ); + return restApi + .updateAccountProtectionSettings( { + jetpack_account_protection_strict_mode: newSettings.strictMode, + } ) + .then( settings => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS, + settings, + } ); + return settings; + } ) + .catch( error => { + dispatch( { + type: ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL, + error: error, + } ); + + throw error; + } ); + }; +}; diff --git a/projects/plugins/jetpack/_inc/client/state/account-protection/index.js b/projects/plugins/jetpack/_inc/client/state/account-protection/index.js new file mode 100644 index 0000000000000..5e3164b4c9f72 --- /dev/null +++ b/projects/plugins/jetpack/_inc/client/state/account-protection/index.js @@ -0,0 +1,2 @@ +export * from './reducer'; +export * from './actions'; diff --git a/projects/plugins/jetpack/_inc/client/state/account-protection/reducer.js b/projects/plugins/jetpack/_inc/client/state/account-protection/reducer.js new file mode 100644 index 0000000000000..cb42d7bccc486 --- /dev/null +++ b/projects/plugins/jetpack/_inc/client/state/account-protection/reducer.js @@ -0,0 +1,87 @@ +import { assign, get } from 'lodash'; +import { combineReducers } from 'redux'; +import { + ACCOUNT_PROTECTION_SETTINGS_FETCH, + ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE, + ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL, + ACCOUNT_PROTECTION_SETTINGS_UPDATE, + ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS, + ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL, +} from 'state/action-types'; + +export const data = ( state = {}, action ) => { + switch ( action.type ) { + case ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE: + case ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS: + return assign( {}, state, { + strictMode: Boolean( action.settings?.jetpack_account_protection_strict_mode ), + } ); + default: + return state; + } +}; + +export const initialRequestsState = { + isFetchingAccountProtectionSettings: false, + isUpdatingAccountProtectionSettings: false, +}; + +export const requests = ( state = initialRequestsState, action ) => { + switch ( action.type ) { + case ACCOUNT_PROTECTION_SETTINGS_FETCH: + return assign( {}, state, { + isFetchingAccountProtectionSettings: true, + } ); + case ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE: + case ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL: + return assign( {}, state, { + isFetchingAccountProtectionSettings: false, + } ); + case ACCOUNT_PROTECTION_SETTINGS_UPDATE: + return assign( {}, state, { + isUpdatingAccountProtectionSettings: true, + } ); + case ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS: + case ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL: + return assign( {}, state, { + isUpdatingAccountProtectionSettings: false, + } ); + default: + return state; + } +}; + +export const reducer = combineReducers( { + data, + requests, +} ); + +/** + * Returns true if currently requesting the account protection settings. Otherwise false. + * + * @param {object} state - Global state tree + * @return {boolean} Whether the account protection settings are being requested + */ +export function isFetchingAccountProtectionSettings( state ) { + return !! state.jetpack.accountProtection.requests.isFetchingAccountProtectionSettings; +} + +/** + * Returns true if currently updating the account protection settings. Otherwise false. + * + * @param {object} state - Global state tree + * @return {boolean} Whether the account protection settings are being requested + */ +export function isUpdatingAccountProtectionSettings( state ) { + return !! state.jetpack.accountProtection.requests.isUpdatingAccountProtectionSettings; +} + +/** + * Returns the account protection's settings. + * + * @param {object} state - Global state tree + * @return {string} File path to bootstrap.php + */ +export function getAccountProtectionSettings( state ) { + return get( state.jetpack.accountProtection, [ 'data' ], {} ); +} diff --git a/projects/plugins/jetpack/_inc/client/state/action-types.js b/projects/plugins/jetpack/_inc/client/state/action-types.js index c4785d4a2ced5..7da1fbb07cf1d 100644 --- a/projects/plugins/jetpack/_inc/client/state/action-types.js +++ b/projects/plugins/jetpack/_inc/client/state/action-types.js @@ -245,6 +245,15 @@ export const JETPACK_LICENSING_GET_USER_LICENSES_FAILURE = export const JETPACK_CONNECTION_HAS_SEEN_WC_CONNECTION_MODAL = 'JETPACK_CONNECTION_HAS_SEEN_WC_CONNECTION_MODAL'; +export const ACCOUNT_PROTECTION_SETTINGS_FETCH = 'ACCOUNT_PROTECTION_SETTINGS_FETCH'; +export const ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE = + 'ACCOUNT_PROTECTION_SETTINGS_FETCH_RECEIVE'; +export const ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL = 'ACCOUNT_PROTECTION_SETTINGS_FETCH_FAIL'; +export const ACCOUNT_PROTECTION_SETTINGS_UPDATE = 'ACCOUNT_PROTECTION_SETTINGS_UPDATE'; +export const ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS = + 'ACCOUNT_PROTECTION_SETTINGS_UPDATE_SUCCESS'; +export const ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL = 'ACCOUNT_PROTECTION_SETTINGS_UPDATE_FAIL'; + export const WAF_SETTINGS_FETCH = 'WAF_SETTINGS_FETCH'; export const WAF_SETTINGS_FETCH_RECEIVE = 'WAF_SETTINGS_FETCH_RECEIVE'; export const WAF_SETTINGS_FETCH_FAIL = 'WAF_SETTINGS_FETCH_FAIL'; diff --git a/projects/plugins/jetpack/_inc/client/state/reducer.js b/projects/plugins/jetpack/_inc/client/state/reducer.js index 5ff156b807a49..14e85f0fb5289 100644 --- a/projects/plugins/jetpack/_inc/client/state/reducer.js +++ b/projects/plugins/jetpack/_inc/client/state/reducer.js @@ -1,5 +1,6 @@ import { combineReducers } from 'redux'; import { globalNotices } from 'components/global-notices/state/notices/reducer'; +import { reducer as accountProtection } from 'state/account-protection/reducer'; import { dashboard } from 'state/at-a-glance/reducer'; import { reducer as connection } from 'state/connection/reducer'; import { reducer as devCard } from 'state/dev-version/reducer'; @@ -46,6 +47,7 @@ const jetpackReducer = combineReducers( { disconnectSurvey, trackingSettings, licensing, + accountProtection, waf, introOffers, } ); diff --git a/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php b/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php index e56d427c0c5ea..9697a1d79d423 100644 --- a/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php +++ b/projects/plugins/jetpack/_inc/lib/class.core-rest-api-endpoints.php @@ -2358,7 +2358,14 @@ public static function get_updateable_data_list( $selector = '' ) { 'validate_callback' => __CLASS__ . '::validate_posint', 'jp_group' => 'custom-content-types', ), - + // Account Protection. + 'jetpack_account_protection_strict_mode' => array( + 'description' => esc_html__( 'Strict mode - Require strong passwords.', 'jetpack' ), + 'type' => 'boolean', + 'default' => 0, + 'validate_callback' => __CLASS__ . '::validate_boolean', + 'jp_group' => 'account-protection', + ), // WAF. 'jetpack_waf_automatic_rules' => array( 'description' => esc_html__( 'Enable automatic rules - Protect your site against untrusted traffic sources with automatic security rules.', 'jetpack' ), diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php index 9852478a7c53d..4b931dadb330f 100644 --- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php +++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php @@ -127,6 +127,7 @@ 'jetpack_subscriptions_login_navigation_enabled' => '(bool) Whether the Subscriber Login block navigation placement is enabled', 'jetpack_subscriptions_subscribe_navigation_enabled' => '(Bool) Whether the Subscribe block navigation placement is enabled', 'wpcom_ai_site_prompt' => '(string) User input in the AI site prompt', + 'jetpack_account_protection_strict_mode' => '(bool) Whether to enforce strict password requirements', 'jetpack_waf_automatic_rules' => '(bool) Whether the WAF should enforce automatic firewall rules', 'jetpack_waf_ip_allow_list' => '(string) List of IP addresses to always allow', 'jetpack_waf_ip_allow_list_enabled' => '(bool) Whether the IP allow list is enabled', @@ -490,6 +491,7 @@ function ( $newsletter_category ) { 'jetpack_comment_form_color_scheme' => (string) get_option( 'jetpack_comment_form_color_scheme' ), 'in_site_migration_flow' => (string) get_option( 'in_site_migration_flow', '' ), 'migration_source_site_domain' => (string) get_option( 'migration_source_site_domain' ), + 'jetpack_account_protection_strict_mode' => (bool) get_option( 'jetpack_account_protection_strict_mode' ), 'jetpack_waf_automatic_rules' => (bool) get_option( 'jetpack_waf_automatic_rules' ), 'jetpack_waf_ip_allow_list' => (string) get_option( 'jetpack_waf_ip_allow_list' ), 'jetpack_waf_ip_allow_list_enabled' => (bool) get_option( 'jetpack_waf_ip_allow_list_enabled' ), diff --git a/projects/plugins/jetpack/modules/waf.php b/projects/plugins/jetpack/modules/waf.php index 1d5a5984f4bab..0df3856fb1948 100644 --- a/projects/plugins/jetpack/modules/waf.php +++ b/projects/plugins/jetpack/modules/waf.php @@ -1,7 +1,7 @@ Date: Wed, 15 Jan 2025 06:29:22 -0800 Subject: [PATCH 09/20] changelog --- .../add-jetpack-account-protection-security-settings | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/js-packages/api/changelog/add-jetpack-account-protection-security-settings diff --git a/projects/js-packages/api/changelog/add-jetpack-account-protection-security-settings b/projects/js-packages/api/changelog/add-jetpack-account-protection-security-settings new file mode 100644 index 0000000000000..778ccde6854ed --- /dev/null +++ b/projects/js-packages/api/changelog/add-jetpack-account-protection-security-settings @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Adds Account Protection requests From 7634ed26a0a64f057a29a51de7a9b258bb93c4a5 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 10:06:39 -0800 Subject: [PATCH 10/20] Use dynamic classes --- .../src/class-account-protection.php | 26 +++++++++---------- .../src/class-rest-controller.php | 20 +++++++------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index f7df6a27ca5fd..69cc546a74533 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -21,10 +21,10 @@ class Account_Protection { /** * Initializes the configurations needed for the account protection module. */ - public static function init() { + public function init() { // 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' ) ); // Register REST routes add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) ); @@ -33,14 +33,14 @@ public static function init() { /** * Activate the account protection on module activation. */ - public static function on_account_protection_activation() { + public function on_account_protection_activation() { // Account protection activated } /** * Deactivate the account protection on module activation. */ - public static function on_account_protection_deactivation() { + public function on_account_protection_deactivation() { // Account protection deactivated } @@ -49,8 +49,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 ( new Modules() )->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); } /** @@ -58,12 +58,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 ( new Modules() )->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); } /** @@ -71,11 +71,11 @@ public static function enable() { * * @return bool */ - public static function disable() { + public function disable() { // Return true if already disabled. - if ( ! self::is_enabled() ) { + if ( ! $this->is_enabled() ) { return true; } - return ( new Modules() )->deactivate( 'account-protection' ); + return ( new Modules() )->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); } } diff --git a/projects/packages/account-protection/src/class-rest-controller.php b/projects/packages/account-protection/src/class-rest-controller.php index 60e6f71f6cc74..998f150941873 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,7 +57,7 @@ public static function register_rest_routes() { * * @return WP_REST_Response */ - public static function get_settings() { + public function get_settings() { return rest_ensure_response( array( Account_Protection::STRICT_MODE_OPTION_NAME => get_option( Account_Protection::STRICT_MODE_OPTION_NAME ), @@ -72,13 +72,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(); } /** @@ -86,7 +86,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; } From 692db33338b4663ecc46532d6725465377fa11cb Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 10:12:46 -0800 Subject: [PATCH 11/20] Update class dependencies --- .../src/class-account-protection.php | 22 ++++++++++++++++--- .../jetpack/modules/account-protection.php | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index 69cc546a74533..e08d0212867f0 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -18,6 +18,22 @@ class Account_Protection { const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection'; const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode'; + /** + * Modules dependency. + * + * @var Modules + */ + private $modules; + + /** + * Constructor. + * + * @param Modules|null $modules Modules dependency. + */ + public function __construct( Modules $modules = null ) { + $this->modules = $modules ?? new Modules(); + } + /** * Initializes the configurations needed for the account protection module. */ @@ -50,7 +66,7 @@ public function on_account_protection_deactivation() { * @return bool */ public function is_enabled() { - return ( new Modules() )->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return $this->modules->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); } /** @@ -63,7 +79,7 @@ public function enable() { if ( $this->is_enabled() ) { return true; } - return ( new Modules() )->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); + return $this->modules->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); } /** @@ -76,6 +92,6 @@ public function disable() { if ( ! $this->is_enabled() ) { return true; } - return ( new Modules() )->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return $this->modules->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); } } 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(); From 22d267848fa4b12690cda417ab80ad5b4518c2aa Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 10:29:37 -0800 Subject: [PATCH 12/20] Fix copy --- .../jetpack/_inc/client/security/account-protection.jsx | 2 +- .../ai-assistant-plugin/components/breve/features/events.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/plugins/jetpack/_inc/client/security/account-protection.jsx b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx index 6b5e9126b7493..7334eb9e3ca7d 100644 --- a/projects/plugins/jetpack/_inc/client/security/account-protection.jsx +++ b/projects/plugins/jetpack/_inc/client/security/account-protection.jsx @@ -142,7 +142,7 @@ const AccountProtection = class extends Component { label={
- { __( 'Reqiure strong passwords', 'jetpack' ) } + { __( 'Require strong passwords', 'jetpack' ) } { ( dispatch( 'jetpack/ai-breve' ) as BreveDispatch ).setHighlightHover( false ); - }, 100 ); + }, 100 ) as unknown as number; } export default function registerEvents( clientId: string ) { From 0fd3e4160b5cc1871b813aa56f706bd93509164e Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 10:30:43 -0800 Subject: [PATCH 13/20] Revert unrelated changes --- .../ai-assistant-plugin/components/breve/features/events.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/breve/features/events.ts b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/breve/features/events.ts index eb5eb0442d2e4..58075d8857569 100644 --- a/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/breve/features/events.ts +++ b/projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/components/breve/features/events.ts @@ -97,7 +97,7 @@ async function handleMouseEnter( e: MouseEvent ) { target: el, virtual: virtual, } as Anchor ); - }, 500 ) as unknown as number; + }, 500 ); } function handleMouseLeave() { @@ -106,7 +106,7 @@ function handleMouseLeave() { highlightTimeout = setTimeout( () => { ( dispatch( 'jetpack/ai-breve' ) as BreveDispatch ).setHighlightHover( false ); - }, 100 ) as unknown as number; + }, 100 ); } export default function registerEvents( clientId: string ) { From 8356bd43747057556614134b54bf18034d8f2251 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 11:22:21 -0800 Subject: [PATCH 14/20] Fix phan errors --- .../src/class-account-protection.php | 22 +++---------------- .../src/class-rest-controller.php | 12 +++++++--- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index e08d0212867f0..69cc546a74533 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -18,22 +18,6 @@ class Account_Protection { const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection'; const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode'; - /** - * Modules dependency. - * - * @var Modules - */ - private $modules; - - /** - * Constructor. - * - * @param Modules|null $modules Modules dependency. - */ - public function __construct( Modules $modules = null ) { - $this->modules = $modules ?? new Modules(); - } - /** * Initializes the configurations needed for the account protection module. */ @@ -66,7 +50,7 @@ public function on_account_protection_deactivation() { * @return bool */ public function is_enabled() { - return $this->modules->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return ( new Modules() )->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); } /** @@ -79,7 +63,7 @@ public function enable() { if ( $this->is_enabled() ) { return true; } - return $this->modules->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); + return ( new Modules() )->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); } /** @@ -92,6 +76,6 @@ public function disable() { if ( ! $this->is_enabled() ) { return true; } - return $this->modules->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return ( new Modules() )->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); } } diff --git a/projects/packages/account-protection/src/class-rest-controller.php b/projects/packages/account-protection/src/class-rest-controller.php index 998f150941873..762fb90570c30 100644 --- a/projects/packages/account-protection/src/class-rest-controller.php +++ b/projects/packages/account-protection/src/class-rest-controller.php @@ -17,6 +17,13 @@ * Defines our endponts. */ class REST_Controller { + /** + * Tracks whether routes have already been registered. + * + * @var bool + */ + private $routes_registered = false; + /** * Register REST API endpoints. * @@ -24,8 +31,7 @@ class REST_Controller { */ public function register_rest_routes() { // Ensure routes are only initialized once. - $routes_registered = false; - if ( $routes_registered ) { + if ( $this->routes_registered ) { return; } @@ -49,7 +55,7 @@ public function register_rest_routes() { ) ); - $routes_registered = true; + $this->routes_registered = true; } /** From 32f3ef6b8a28580fbf9b285c5b8343ba98d5578e Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 13:36:55 -0800 Subject: [PATCH 15/20] Changelog --- .../add-jetpack-account-protection-security-settings | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/packages/account-protection/changelog/add-jetpack-account-protection-security-settings diff --git a/projects/packages/account-protection/changelog/add-jetpack-account-protection-security-settings b/projects/packages/account-protection/changelog/add-jetpack-account-protection-security-settings new file mode 100644 index 0000000000000..af516388c3c6c --- /dev/null +++ b/projects/packages/account-protection/changelog/add-jetpack-account-protection-security-settings @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Adds handling for module activation and deactivation From b02d5113d62f05649926e02ff966722cb7dfbb01 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 13:57:12 -0800 Subject: [PATCH 16/20] Update composer deps --- projects/packages/account-protection/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/packages/account-protection/composer.json b/projects/packages/account-protection/composer.json index b6a0271497be0..42431a12e7a10 100644 --- a/projects/packages/account-protection/composer.json +++ b/projects/packages/account-protection/composer.json @@ -4,7 +4,9 @@ "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { - "php": ">=7.2" + "php": ">=7.2", + "automattic/jetpack-connection": "@dev", + "automattic/jetpack-status": "@dev" }, "require-dev": { "yoast/phpunit-polyfills": "^1.1.1", From 7c255ac1b616930b01ee5e5a03f926aef603e3ea Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 14:02:24 -0800 Subject: [PATCH 17/20] Update lock files, add constructor method --- .../src/class-account-protection.php | 22 ++++++++++++++++--- projects/plugins/jetpack/composer.lock | 4 +++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index 69cc546a74533..7d2547f9566fb 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -18,6 +18,22 @@ class Account_Protection { const ACCOUNT_PROTECTION_MODULE_NAME = 'account-protection'; const STRICT_MODE_OPTION_NAME = 'jetpack_account_protection_strict_mode'; + /** + * Modules instance. + * + * @var Modules + */ + private $modules; + + /** + * Account_Protection constructor. + * + * @param Modules $modules Modules instance. + */ + public function __construct( Modules $modules = null ) { + $this->modules = $modules ?? new Modules(); + } + /** * Initializes the configurations needed for the account protection module. */ @@ -50,7 +66,7 @@ public function on_account_protection_deactivation() { * @return bool */ public function is_enabled() { - return ( new Modules() )->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return $this->modules->is_active( self::ACCOUNT_PROTECTION_MODULE_NAME ); } /** @@ -63,7 +79,7 @@ public function enable() { if ( $this->is_enabled() ) { return true; } - return ( new Modules() )->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); + return $this->modules->activate( self::ACCOUNT_PROTECTION_MODULE_NAME, false, false ); } /** @@ -76,6 +92,6 @@ public function disable() { if ( ! $this->is_enabled() ) { return true; } - return ( new Modules() )->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); + return $this->modules->deactivate( self::ACCOUNT_PROTECTION_MODULE_NAME ); } } diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index f9e12cf1d5ec8..5c4a01cd5a9f0 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -65,9 +65,11 @@ "dist": { "type": "path", "url": "../../packages/account-protection", - "reference": "c22829e6a80ff9f5cd10e4b4eece3d405f69e8f9" + "reference": "badc1036552f26a900a69608df22284e603981ed" }, "require": { + "automattic/jetpack-connection": "@dev", + "automattic/jetpack-status": "@dev", "php": ">=7.2" }, "require-dev": { From cdb0ac8bea39d29a541a657c2d31bc3f68ae2d4b Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 14:07:52 -0800 Subject: [PATCH 18/20] Fix php warning --- .../account-protection/src/class-account-protection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/packages/account-protection/src/class-account-protection.php b/projects/packages/account-protection/src/class-account-protection.php index 7d2547f9566fb..745900f5d11a2 100644 --- a/projects/packages/account-protection/src/class-account-protection.php +++ b/projects/packages/account-protection/src/class-account-protection.php @@ -28,9 +28,9 @@ class Account_Protection { /** * Account_Protection constructor. * - * @param Modules $modules Modules instance. + * @param ?Modules $modules Modules instance. */ - public function __construct( Modules $modules = null ) { + public function __construct( ?Modules $modules = null ) { $this->modules = $modules ?? new Modules(); } From ae3b6b6722ab9915b6e1d8cdc02666d5cc9276f6 Mon Sep 17 00:00:00 2001 From: dkmyta Date: Mon, 20 Jan 2025 15:46:42 -0800 Subject: [PATCH 19/20] Update @package --- projects/packages/account-protection/tests/php/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/packages/account-protection/tests/php/bootstrap.php b/projects/packages/account-protection/tests/php/bootstrap.php index 46763b04a2cdb..c53f9cb5415c3 100644 --- a/projects/packages/account-protection/tests/php/bootstrap.php +++ b/projects/packages/account-protection/tests/php/bootstrap.php @@ -2,7 +2,7 @@ /** * Bootstrap. * - * @package automattic/ + * @package automattic/jetpack-account-protection */ /** From aad7ff6c09bf85a041e1d059b20ade9aa97e2b8e Mon Sep 17 00:00:00 2001 From: dkmyta Date: Tue, 21 Jan 2025 12:52:21 -0800 Subject: [PATCH 20/20] Enable module by default --- projects/plugins/jetpack/modules/account-protection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/modules/account-protection.php b/projects/plugins/jetpack/modules/account-protection.php index 554570f666289..c552efec4cc41 100644 --- a/projects/plugins/jetpack/modules/account-protection.php +++ b/projects/plugins/jetpack/modules/account-protection.php @@ -6,7 +6,7 @@ * First Introduced: 14.3 * Requires Connection: Yes * Requires User Connection: No - * Auto Activate: No + * Auto Activate: Yes * Module Tags: Account Protection * Feature: Security *