Skip to content

Commit

Permalink
Jetpack E2E: add account protection tests (#41835)
Browse files Browse the repository at this point in the history
  • Loading branch information
nateweller committed Feb 20, 2025
1 parent 271839a commit 7e5a993
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 0 deletions.
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/add-account-protection-e2e
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
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;
}
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' );
} );
} );

0 comments on commit 7e5a993

Please sign in to comment.