-
Notifications
You must be signed in to change notification settings - Fork 813
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Jetpack E2E: add account protection tests (#41835)
- Loading branch information
1 parent
271839a
commit 7e5a993
Showing
3 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/jetpack/changelog/add-account-protection-e2e
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: patch | ||
Type: other | ||
|
||
E2E Tests: added test coverage for account protection features |
51 changes: 51 additions & 0 deletions
51
projects/plugins/jetpack/tests/e2e/helpers/account-protection-helper.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { execWpCommand } from '_jetpack-e2e-commons/helpers/utils-helper.js'; | ||
import logger from '_jetpack-e2e-commons/logger.js'; | ||
|
||
const PRIVILEGED_ROLES = [ 'administrator', 'editor', 'author' ]; | ||
const NON_PRIVILEGED_ROLES = [ 'contributor', 'subscriber' ]; | ||
|
||
/** | ||
* Enable automatic rules | ||
* @return {Promise<void>} wp-cli 'jetpack-waf generate_rules' command output | ||
*/ | ||
export async function insertTestUsers() { | ||
logger.sync( 'Inserting test users' ); | ||
|
||
// Create user accounts with compromised passwords. | ||
for ( const role of [ ...PRIVILEGED_ROLES, ...NON_PRIVILEGED_ROLES ] ) { | ||
await execWpCommand( | ||
`user create ${ role } ${ role }@example.com --role=${ role } --user_pass=password` | ||
); | ||
} | ||
|
||
// Create a user with a secure password. | ||
await execWpCommand( | ||
`user create secure_user [email protected] --role=administrator --user_pass=87h23foi2uhfljhdakdh9812df` | ||
); | ||
} | ||
|
||
/** | ||
* Get account protection token from URL | ||
* @param {string} url - The URL to get the token from | ||
* @return {string} account protection token | ||
*/ | ||
export function getAccountProtectionTokenFromUrl( url ) { | ||
const queryParams = new URLSearchParams( url.split( '?' )[ 1 ] ); | ||
return queryParams.get( 'token' ); | ||
} | ||
|
||
/** | ||
* Get account protection auth code from transient | ||
* @param {string} token - The token to get the auth code from | ||
* @return {Promise<string>} account protection auth code | ||
*/ | ||
export async function getAccountProtectionAuthCodeFromTransient( token ) { | ||
const transient = await execWpCommand( | ||
`transient get password_detection_${ token } --format=json` | ||
); | ||
logger.info( `Transient: ${ transient }` ); | ||
console.log( `Transient: ${ transient }` ); | ||
const { auth_code } = JSON.parse( transient ); | ||
|
||
return auth_code; | ||
} |
147 changes: 147 additions & 0 deletions
147
projects/plugins/jetpack/tests/e2e/specs/post-connection/account-protection.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { prerequisitesBuilder } from '_jetpack-e2e-commons/env/index.js'; | ||
import { test, expect } from '_jetpack-e2e-commons/fixtures/base-test.js'; | ||
import { WPLoginPage } from '_jetpack-e2e-commons/pages/wp-admin/index.js'; | ||
import { | ||
getAccountProtectionAuthCodeFromTransient, | ||
getAccountProtectionTokenFromUrl, | ||
insertTestUsers, | ||
} from '../../helpers/account-protection-helper.js'; | ||
import playwrightConfig from '../../playwright.config.mjs'; | ||
|
||
const PRIVILEGED_ROLES = [ 'administrator', 'editor', 'author' ]; | ||
const NON_PRIVILEGED_ROLES = [ 'contributor', 'subscriber' ]; | ||
|
||
test.describe.parallel( 'Compromised Password Detection', () => { | ||
test.beforeAll( async ( { browser } ) => { | ||
// Set up a clean environment with account protection enabled. | ||
const page = await browser.newPage( playwrightConfig.use ); | ||
await prerequisitesBuilder( page ) | ||
.withInactiveModules( [ 'protect', 'sso' ] ) | ||
.withActiveModules( [ 'account-protection' ] ) | ||
.withCleanEnv() | ||
.withConnection( true ) | ||
.build(); | ||
|
||
await insertTestUsers(); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
test( 'Detects compromised passwords', async ( { page } ) => { | ||
for ( const role of PRIVILEGED_ROLES ) { | ||
await test.step( `Enforces account protection 2FA for ${ role } users`, async () => { | ||
const loginPage = await WPLoginPage.visit( page ); | ||
|
||
// Attempt sign in. | ||
await loginPage.fill( '#user_login', role ); | ||
await loginPage.fill( '#user_pass', 'password' ); | ||
await loginPage.click( '#wp-submit' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeVisible( '.action-input' ); | ||
|
||
expect( page.url() ).toContain( 'token=' ); | ||
|
||
// Get the token and auth code. | ||
const token = getAccountProtectionTokenFromUrl( page.url() ); | ||
const authCode = await getAccountProtectionAuthCodeFromTransient( token ); | ||
|
||
expect( authCode ).toBeTruthy(); | ||
|
||
// Submit the auth code. | ||
await loginPage.fill( '.action-input', authCode ); | ||
await loginPage.click( '.action-verify' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeVisible( '.action-proceed' ); | ||
|
||
// Proceed to wp-admin. | ||
await loginPage.click( '.action-proceed' ); | ||
|
||
// Wait for the navigation to complete. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeHidden( '.action-proceed' ); | ||
|
||
expect( page.url() ).toContain( '/wp-admin' ); | ||
|
||
// Sign out. | ||
const accountBarSelector = '#wp-admin-bar-my-account'; | ||
const logoutOptionSelector = '#wp-admin-bar-logout'; | ||
await loginPage.waitForElementToBeVisible( accountBarSelector ); | ||
await loginPage.hover( accountBarSelector ); | ||
await loginPage.click( logoutOptionSelector ); | ||
} ); | ||
} | ||
|
||
for ( const role of NON_PRIVILEGED_ROLES ) { | ||
await test.step( `Bypasses account protection 2FA for ${ role } users`, async () => { | ||
const loginPage = await WPLoginPage.visit( page ); | ||
|
||
// Attempt sign in. | ||
await loginPage.fill( '#user_login', role ); | ||
await loginPage.fill( '#user_pass', 'password' ); | ||
await loginPage.click( '#wp-submit' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeHidden( loginPage.selectors[ 0 ] ); | ||
|
||
expect( page.url() ).toContain( '/wp-admin' ); | ||
} ); | ||
} | ||
|
||
await test.step( `Bypasses account protection 2FA for users with secure passwords`, async () => { | ||
const loginPage = await WPLoginPage.visit( page ); | ||
|
||
// Attempt sign in. | ||
await loginPage.fill( '#user_login', 'secure_user' ); | ||
await loginPage.fill( '#user_pass', '87h23foi2uhfljhdakdh9812df' ); | ||
await loginPage.click( '#wp-submit' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeHidden( loginPage.selectors[ 0 ] ); | ||
|
||
// Test successful sign in. | ||
expect( page.url() ).toContain( '/wp-admin' ); | ||
} ); | ||
} ); | ||
|
||
test( 'Password reset after verification', async ( { page } ) => { | ||
const loginPage = await WPLoginPage.visit( page ); | ||
|
||
// Attempt sign in. | ||
await loginPage.fill( '#user_login', 'administrator' ); | ||
await loginPage.fill( '#user_pass', 'password' ); | ||
await loginPage.click( '#wp-submit' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeVisible( '.action-input' ); | ||
|
||
expect( page.url() ).toContain( 'token=' ); | ||
|
||
// Get the token and auth code. | ||
const token = getAccountProtectionTokenFromUrl( page.url() ); | ||
const authCode = await getAccountProtectionAuthCodeFromTransient( token ); | ||
|
||
// Submit the auth code. | ||
await loginPage.fill( '.action-input', authCode ); | ||
await loginPage.click( '.action-verify' ); | ||
|
||
// Wait for the form submission. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeVisible( '.action-update-password' ); | ||
|
||
// Choose to update the password. | ||
await loginPage.click( '.action-update-password' ); | ||
|
||
// Wait for the navigation to complete. | ||
await loginPage.waitForDomContentLoaded(); | ||
await loginPage.waitForElementToBeHidden( '.action-update-password' ); | ||
|
||
expect( page.url() ).toContain( '/profile.php#password' ); | ||
} ); | ||
} ); |