Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CANTINA-958: Add better filtering in wp vip two-factor report CLI command #4910

Merged
merged 3 commits into from
Oct 3, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 113 additions & 6 deletions wp-cli/vip-two-factor.php
Original file line number Diff line number Diff line change
@@ -1,35 +1,142 @@
<?php

class VIP_Two_Factor_Command extends WPCOM_VIP_CLI_Command {
/**
* Reports on the 2FA status for users of a site. Lists all users by default.
* Note: Users without `manage_options` cap will be listed as "n/a" for 2FA status.
*
* ## OPTIONS
*
* [--2fa-enabled=<true|false>]
* : Filter by whether 2FA is enabled or not.
*
* [--role=<string>]
* : Filter by user role.
*
* [--2fa-provider=<string>]
* : Filter by 2FA provider. Accepts values of: email, totp, fido_u2f, backup_codes, dummy
*
* [--user_login=<login|id|email>]
* : Filter by user login, ID, or email.
*
*
* ## EXAMPLES
*
* # List 2FA status for all users.
* $ wp vip two-factor report
*
* # List users with 2FA enabled.
* $ wp vip two-factor report --2fa-enabled=true
*
* # List users who use email as their authentication factor.
* $ wp vip two-factor report --2fa-provider=email
*
* # List 2FA status for administrators.
* $ wp vip two-factor report --role=administrator
*
* # List administrators who don't have 2FA enabled.
* $ wp vip two-factor report --role=administrator --2fa-enabled=false
*
* # List 2FA status for administrators using the email authentication factor.
* $ wp vip two-factor report --role=administrator --2fa-enabled=true --2fa-provider=email
*
* # List 2FA status only for the wpvip user.
* $ wp vip two-factor report --user_login=wpvip
*
* @synopsis [--2fa-enabled=<true|false>] [--role=<string>] [--2fa-provider=<string>] [--user_login=<login|id|email>]
*/
public function report( $args, $assoc_args ) {
if ( ! apply_filters( 'wpcom_vip_enable_two_factor', true ) ) {
WP_CLI::error( 'This site has disabled Two Factor.' );
}

$format = \WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );
$fields = array( 'ID', 'display_name', 'roles' );
$twofa_enabled_flag = \WP_CLI\Utils\get_flag_value( $assoc_args, '2fa-enabled', null );
$role_flag = \WP_CLI\Utils\get_flag_value( $assoc_args, 'role', null );
$twofa_provider_flag = strtolower( \WP_CLI\Utils\get_flag_value( $assoc_args, '2fa-provider', null ) );
$user_id_flag = \WP_CLI\Utils\get_flag_value( $assoc_args, 'user_login', null );
$format = \WP_CLI\Utils\get_flag_value( $assoc_args, 'format', 'table' );
if ( 'false' === $twofa_enabled_flag && $twofa_provider_flag ) {
WP_CLI::error( 'Cannot filter by Two Factor provider when searching for users without Two Factor.' );
}


$providers = Two_Factor_Core::get_providers();
$default_provider_map = [
'Two_Factor_Email' => 'email',
'Two_Factor_Totp' => 'totp',
'Two_Factor_FIDO_U2F' => 'fido_u2f',
'Two_Factor_Backup_Codes' => 'backup_codes',
'Two_Factor_Dummy' => 'dummy',
];
$providers_map = array_intersect_key( $default_provider_map, $providers );
if ( $twofa_provider_flag ) {
if ( ! in_array( $twofa_provider_flag, $providers_map, true ) ) {
WP_CLI::error(
sprintf(
'Invalid Two Factor provider "%s". Valid values are: %s',
$twofa_provider_flag,
implode( ', ', array_values( $providers_map ) )
)
);
}

$users = get_users();
$providers = Two_Factor_Core::get_providers();
$twofa_enabled_flag = 'true'; // In case `--2fa-enabled` was not passed in.
$twofa_provider_flag = array_search( $twofa_provider_flag, $providers_map, true );
}

$cap = apply_filters( 'wpcom_vip_two_factor_enforcement_cap', 'manage_options' );
foreach ( $users as $user ) {

if ( $user_id_flag ) {
if ( is_numeric( $user_id_flag ) ) {
$user = get_user_by( 'id', $user_id_flag );
} else {
$user = get_user_by( 'login', $user_id_flag );
if ( ! $user ) {
$user = get_user_by( 'email', $user_id_flag );
}
}
if ( ! $user ) {
WP_CLI::error( sprintf( 'User "%s" not found.', $user_id_flag ) );
}

$users = [ $user ];
} else {
$user_args = [];
if ( $twofa_enabled_flag ) {
$user_args['capability__in'] = $cap;
}
if ( $role_flag ) {
$user_args['role'] = $role_flag;
}

$users = get_users( $user_args );
}

foreach ( $users as $idx => $user ) {
$user->two_factor_enabled = 'false';
$user->two_factor_providers = '';

if ( Two_Factor_Core::is_user_using_two_factor( $user->ID ) ) {
$user->two_factor_enabled = 'true';

$user_providers = Two_Factor_Core::get_enabled_providers_for_user( $user );
$user_providers = Two_Factor_Core::get_enabled_providers_for_user( $user );
if ( $twofa_provider_flag && ! in_array( $twofa_provider_flag, $user_providers, true ) ) {
unset( $users[ $idx ] );
}
$user_providers = array_map( function ( $provider ) use ( $providers ) {
return $providers[ $provider ]->get_label();
}, $user_providers );
$user->two_factor_providers = implode( ', ', $user_providers );
} elseif ( ! user_can( $user->ID, $cap ) ) {
$user->two_factor_enabled = 'n/a';
}

if ( $twofa_enabled_flag && $user->two_factor_enabled !== $twofa_enabled_flag ) {
unset( $users[ $idx ] );
}
}

$fields = array( 'ID', 'display_name', 'roles' );
$fields[] = 'two_factor_enabled';
$fields[] = 'two_factor_providers';
Comment on lines +139 to 141
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$fields = array( 'ID', 'display_name', 'roles' );
$fields[] = 'two_factor_enabled';
$fields[] = 'two_factor_providers';
$fields = array( 'ID', 'display_name', 'roles', 'two_factor_enabled', 'two_factor_providers' );

Further, looking at the output from wp user list --format=table:

+----+------------------+-------------------+-----------------------------------------+---------------------+---------------+
| ID | user_login       | display_name      | user_email                              | user_registered     | roles         |
+----+------------------+-------------------+-----------------------------------------+---------------------+---------------+

...then maybe this report output should also include the user_login, after the ID?
(May be outside the scope of this PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, I don't think it's necessary given the user lookup can be login/id/email.

WP_CLI\Utils\format_items( $format, $users, $fields );
Expand Down
Loading