] * : Filter by user role. * * [--format=] * : Output format (table, json, csv). Default: table. * * ## EXAMPLES * * wp two-factor-extended status * wp two-factor-extended status --role=administrator * wp two-factor-extended status --format=json * * @since 0.1.0 * * @param array $args Positional arguments. * @param array $assoc_args Associative arguments. */ public function status( array $args, array $assoc_args ): void { $role = $assoc_args['role'] ?? ''; $format = $assoc_args['format'] ?? 'table'; $compliance = two_factor_extended()->get_compliance_report(); if ( null === $compliance ) { WP_CLI::error( 'Compliance report module is not available.' ); return; } $stats = $compliance->get_compliance_stats( array( 'role' => $role ) ); if ( 'json' === $format ) { WP_CLI::line( wp_json_encode( $stats, JSON_PRETTY_PRINT ) ); return; } // Display overview. WP_CLI::line( WP_CLI::colorize( '%G2FA Compliance Status%n' ) ); WP_CLI::line( str_repeat( '=', 40 ) ); $overview = array( array( 'Metric' => 'Total Users', 'Count' => $stats['total_users'], 'Percentage' => '100%', ), array( 'Metric' => 'Compliant', 'Count' => $stats['compliant_users'], 'Percentage' => $this->calculate_percentage( $stats['compliant_users'], $stats['total_users'] ), ), array( 'Metric' => 'Non-Compliant', 'Count' => $stats['non_compliant'], 'Percentage' => $this->calculate_percentage( $stats['non_compliant'], $stats['total_users'] ), ), array( 'Metric' => 'In Grace Period', 'Count' => $stats['grace_period'], 'Percentage' => $this->calculate_percentage( $stats['grace_period'], $stats['total_users'] ), ), array( 'Metric' => 'No Requirements', 'Count' => $stats['no_requirements'], 'Percentage' => $this->calculate_percentage( $stats['no_requirements'], $stats['total_users'] ), ), ); WP_CLI\Utils\format_items( $format, $overview, array( 'Metric', 'Count', 'Percentage' ) ); // Display by role if available. if ( ! empty( $stats['by_role'] ) ) { WP_CLI::line( '' ); WP_CLI::line( WP_CLI::colorize( '%GBy Role%n' ) ); WP_CLI::line( str_repeat( '=', 40 ) ); $by_role = array(); foreach ( $stats['by_role'] as $role_key => $role_stats ) { $role_name = Two_Factor_Extended_Role_Manager::get_role_display_name( $role_key ); $by_role[] = array( 'Role' => $role_name, 'Total' => $role_stats['total'], 'Compliant' => $role_stats['compliant'], 'Non-Compliant' => $role_stats['non_compliant'], 'Compliance %' => $this->calculate_percentage( $role_stats['compliant'], $role_stats['total'] ), ); } WP_CLI\Utils\format_items( $format, $by_role, array( 'Role', 'Total', 'Compliant', 'Non-Compliant', 'Compliance %' ) ); } } /** * Enforce 2FA requirements for a role. * * ## OPTIONS * * --role= * : User role to enforce. * * [--reset-grace] * : Reset grace period for all users in role. * * ## EXAMPLES * * wp two-factor-extended enforce --role=administrator * wp two-factor-extended enforce --role=editor --reset-grace * * @since 0.1.0 * * @param array $args Positional arguments. * @param array $assoc_args Associative arguments. */ public function enforce( array $args, array $assoc_args ): void { if ( empty( $assoc_args['role'] ) ) { WP_CLI::error( 'Please specify a role with --role=' ); return; } $role = $assoc_args['role']; $reset_grace = isset( $assoc_args['reset-grace'] ); // Get users with this role. $users = get_users( array( 'role' => $role ) ); if ( empty( $users ) ) { WP_CLI::warning( sprintf( 'No users found with role: %s', $role ) ); return; } $enforcement = two_factor_extended()->get_enforcement(); if ( null === $enforcement ) { WP_CLI::error( 'Enforcement module is not available.' ); return; } $processed = 0; $progress = WP_CLI\Utils\make_progress_bar( 'Enforcing 2FA requirements', count( $users ) ); foreach ( $users as $user ) { $required = $enforcement->get_required_providers_for_user( $user->ID ); if ( empty( $required ) ) { $progress->tick(); continue; } // Set or reset enforcement start date. update_user_meta( $user->ID, Two_Factor_Extended_Enforcement::META_ENFORCEMENT_START, time() ); if ( $reset_grace ) { delete_user_meta( $user->ID, Two_Factor_Extended_Enforcement::META_GRACE_NOTIFIED ); } // Log the action. $audit_log = two_factor_extended()->get_audit_log(); if ( null !== $audit_log ) { $audit_log->log_event( 'cli_enforce', sprintf( 'WP-CLI: Enforced 2FA for user %s', $user->user_login ), $user->ID, array( 'role' => $role, 'required_providers' => $required, ) ); } $processed++; $progress->tick(); } $progress->finish(); WP_CLI::success( sprintf( 'Enforced 2FA requirements for %d users with role: %s', $processed, $role ) ); } /** * Generate compliance report. * * ## OPTIONS * * [--role=] * : Filter by user role. * * [--format=] * : Output format (table, json, csv). Default: table. * * [--non-compliant-only] * : Show only non-compliant users. * * ## EXAMPLES * * wp two-factor-extended report * wp two-factor-extended report --role=administrator * wp two-factor-extended report --non-compliant-only --format=csv * * @since 0.1.0 * * @param array $args Positional arguments. * @param array $assoc_args Associative arguments. */ public function report( array $args, array $assoc_args ): void { $role = $assoc_args['role'] ?? ''; $format = $assoc_args['format'] ?? 'table'; $non_compliant_only = isset( $assoc_args['non-compliant-only'] ); $compliance = two_factor_extended()->get_compliance_report(); if ( null === $compliance ) { WP_CLI::error( 'Compliance report module is not available.' ); return; } if ( $non_compliant_only ) { $users = $compliance->get_non_compliant_users( array( 'role' => $role ) ); if ( empty( $users ) ) { WP_CLI::success( 'All users are compliant!' ); return; } $items = array(); foreach ( $users as $user_data ) { $roles_raw = isset( $user_data['roles'] ) && is_array( $user_data['roles'] ) ? $user_data['roles'] : array(); $missing_providers_raw = isset( $user_data['missing_providers'] ) && is_array( $user_data['missing_providers'] ) ? $user_data['missing_providers'] : array(); $roles = array_filter( $roles_raw, 'is_string' ); $missing_providers = array_filter( $missing_providers_raw, 'is_string' ); $items[] = array( 'User ID' => $user_data['user_id'], 'Username' => $user_data['user_login'], 'Email' => $user_data['user_email'], 'Roles' => implode( ', ', $roles ), 'Missing Providers' => implode( ', ', $missing_providers ), 'Grace Period' => $user_data['in_grace_period'] ? 'Yes' : 'No', 'Days Remaining' => $user_data['grace_remaining'], ); } WP_CLI\Utils\format_items( $format, $items, array( 'User ID', 'Username', 'Email', 'Roles', 'Missing Providers', 'Grace Period', 'Days Remaining' ) ); } else { // Show full statistics. $this->status( $args, $assoc_args ); } } /** * Reset 2FA grace period for a user. * * ## OPTIONS * * --user= * : User ID, login, or email. * * ## EXAMPLES * * wp two-factor-extended reset --user=admin * wp two-factor-extended reset --user=123 * wp two-factor-extended reset --user=admin@example.com * * @since 0.1.0 * * @param array $args Positional arguments. * @param array $assoc_args Associative arguments. */ public function reset( array $args, array $assoc_args ): void { if ( empty( $assoc_args['user'] ) ) { WP_CLI::error( 'Please specify a user with --user=' ); return; } // Get user by ID, login, or email. $user_identifier = $assoc_args['user']; if ( is_numeric( $user_identifier ) ) { $user = get_userdata( (int) $user_identifier ); } elseif ( is_email( $user_identifier ) ) { $user = get_user_by( 'email', $user_identifier ); } else { $user = get_user_by( 'login', $user_identifier ); } if ( ! $user ) { WP_CLI::error( sprintf( 'User not found: %s', $user_identifier ) ); return; } // Reset enforcement start date. update_user_meta( $user->ID, Two_Factor_Extended_Enforcement::META_ENFORCEMENT_START, time() ); // Clear grace period notified flag. delete_user_meta( $user->ID, Two_Factor_Extended_Enforcement::META_GRACE_NOTIFIED ); // Log the action. $audit_log = two_factor_extended()->get_audit_log(); if ( null !== $audit_log ) { $audit_log->log_event( 'cli_reset_grace', sprintf( 'WP-CLI: Reset grace period for user %s', $user->user_login ), $user->ID ); } WP_CLI::success( sprintf( 'Grace period reset for user: %s (ID: %d)', $user->user_login, $user->ID ) ); } /** * Calculate percentage. * * @since 0.1.0 * * @param int $part Part value. * @param int $total Total value. * * @return string Percentage string. */ private function calculate_percentage( int $part, int $total ): string { if ( 0 === $total ) { return '0%'; } return sprintf( '%.1f%%', ( $part / $total ) * 100 ); } }