*/ const APPLICATION_PASSWORD_FIELDS = [ 'uuid', 'app_id', 'name', 'password', 'created', 'last_used', 'last_ip', ]; /** * Lists all application passwords associated with a user. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to get application passwords for. * * [--=] * : Filter the list by a specific field. * * [--field=] * : Prints the value of a single field for each application password. * * [--fields=] * : Limit the output to specific fields. * * [--format=] * : Render output in a particular format. * --- * default: table * options: * - table * - csv * - json * - count * - yaml * - ids * --- * * [--orderby=] * : Set orderby which field. * --- * default: created * options: * - uuid * - app_id * - name * - password * - created * - last_used * - last_ip * --- * * [--order=] * : Set ascending or descending order. * --- * default: desc * options: * - asc * - desc * --- * * ## EXAMPLES * * # List user application passwords and only show app name and password hash * $ wp user application-password list 123 --fields=name,password * +--------+------------------------------------+ * | name | password | * +--------+------------------------------------+ * | myapp | $P$BVGeou1CUot114YohIemgpwxQCzb8O/ | * +--------+------------------------------------+ * * @subcommand list * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the user could not be found/parsed. * @throws ExitException If the application passwords could not be retrieved. */ public function list_( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); list( $user_id ) = $args; $application_passwords = WP_Application_Passwords::get_user_application_passwords( $user_id ); if ( $application_passwords instanceof WP_Error ) { WP_CLI::error( $application_passwords ); } if ( empty( $application_passwords ) ) { $application_passwords = []; } $order = Utils\get_flag_value( $assoc_args, 'order' ); $orderby = Utils\get_flag_value( $assoc_args, 'orderby' ); usort( $application_passwords, static function ( $a, $b ) use ( $orderby, $order ) { // Sort array. return 'asc' === $order ? $a[ $orderby ] > $b[ $orderby ] : $a[ $orderby ] < $b[ $orderby ]; } ); $fields = self::APPLICATION_PASSWORD_FIELDS; // Avoid confusion regarding the dash/underscore usage. foreach ( [ 'app-id', 'last-used', 'last-ip' ] as $flag ) { if ( array_key_exists( $flag, $assoc_args ) ) { $underscored_flag = str_replace( '-', '_', $flag ); $assoc_args[ $underscored_flag ] = $assoc_args[ $flag ]; unset( $assoc_args[ $flag ] ); } } foreach ( $fields as $field ) { if ( ! array_key_exists( $field, $assoc_args ) ) { continue; } $value = Utils\get_flag_value( $assoc_args, $field ); $application_passwords = array_filter( $application_passwords, static function ( $application_password ) use ( $field, $value ) { return $application_password[ $field ] === $value; } ); } if ( ! empty( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); } $format = Utils\get_flag_value( $assoc_args, 'format', 'table' ); $formatter = new Formatter( $assoc_args, $fields ); if ( 'ids' === $format ) { $formatter->display_items( wp_list_pluck( $application_passwords, 'uuid' ) ); } else { $formatter->display_items( $application_passwords ); } } /** * Gets a specific application password. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to get the application password for. * * * : The universally unique ID of the application password. * * [--field=] * : Prints the value of a single field for the application password. * * [--fields=] * : Limit the output to specific fields. * * [--format=] * : Render output in a particular format. * --- * default: table * options: * - table * - csv * - json * - yaml * --- * * ## EXAMPLES * * # Get a specific application password and only show app name and created timestamp * $ wp user application-password get 123 6633824d-c1d7-4f79-9dd5-4586f734d69e --fields=name,created * +--------+------------+ * | name | created | * +--------+------------+ * | myapp | 1638395611 | * +--------+------------+ * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application passwords could not be retrieved. */ public function get( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); list( $user_id, $uuid ) = $args; $application_password = WP_Application_Passwords::get_user_application_password( $user_id, $uuid ); if ( $application_password instanceof WP_Error ) { WP_CLI::error( $application_password ); } if ( null === $application_password ) { WP_CLI::error( 'No application password found for this user ID and UUID.' ); } $fields = self::APPLICATION_PASSWORD_FIELDS; if ( ! empty( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); } $formatter = new Formatter( $assoc_args, $fields ); $formatter->display_items( [ $application_password ] ); } /** * Creates a new application password. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to create a new application password for. * * * : Unique name of the application to create an application password for. * * [--app-id=] * : Application ID to attribute to the application password. * * [--porcelain] * : Output just the new password. * * ## EXAMPLES * * # Create user application password * $ wp user application-password create 123 myapp * Success: Created application password. * Password: ZG1bxdxdzjTwhsY8vK8l1C65 * * # Only print the password without any chrome * $ wp user application-password create 123 myapp --porcelain * ZG1bxdxdzjTwhsY8vK8l1C65 * * # Create user application with a custom application ID for internal tracking * $ wp user application-password create 123 myapp --app-id=42 --porcelain * ZG1bxdxdzjTwhsY8vK8l1C65 * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application password could not be created. */ public function create( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); list( $user_id, $app_name ) = $args; $app_id = Utils\get_flag_value( $assoc_args, 'app-id', '' ); $arguments = [ 'name' => $app_name, 'app_id' => $app_id, ]; $result = WP_Application_Passwords::create_new_application_password( $user_id, $arguments ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); } if ( Utils\get_flag_value( $assoc_args, 'porcelain', false ) ) { WP_CLI::line( $result[0] ); WP_CLI::halt( 0 ); } WP_CLI::success( 'Created application password.' ); WP_CLI::line( "Password: {$result[0]}" ); } /** * Updates an existing application password. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to update the application password for. * * * : The universally unique ID of the application password. * * [--=] * : Update the with a new . Currently supported fields: name. * * ## EXAMPLES * * # Update an existing application password * $ wp user application-password update 123 6633824d-c1d7-4f79-9dd5-4586f734d69e --name=newappname * Success: Updated application password. * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application password could not be created. */ public function update( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); list( $user_id, $uuid ) = $args; $result = WP_Application_Passwords::update_application_password( $user_id, $uuid, $assoc_args ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); } WP_CLI::success( 'Updated application password.' ); } /** * Record usage of an application password. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to update the application password for. * * * : The universally unique ID of the application password. * * ## EXAMPLES * * # Record usage of an application password * $ wp user application-password record-usage 123 6633824d-c1d7-4f79-9dd5-4586f734d69e * Success: Recorded application password usage. * * @subcommand record-usage * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application password could not be created. */ public function record_usage( $args, $assoc_args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $args = $this->replace_login_with_user_id( $args ); list( $user_id, $uuid ) = $args; $result = WP_Application_Passwords::record_application_password_usage( $user_id, $uuid ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); } WP_CLI::success( 'Recorded application password usage.' ); } /** * Delete an existing application password. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to delete the application password for. * * [...] * : Comma-separated list of UUIDs of the application passwords to delete. * * [--all] * : Delete all of the user's application password. * * ## EXAMPLES * * # Delete an existing application password * $ wp user application-password delete 123 6633824d-c1d7-4f79-9dd5-4586f734d69e * Success: Deleted 1 of 1 application password. * * # Delete all of the user's application passwords * $ wp user application-password delete 123 --all * Success: Deleted all application passwords. * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application password could not be created. */ public function delete( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); $user_id = array_shift( $args ); $all = Utils\get_flag_value( $assoc_args, 'all', false ); $count = count( $args ); if ( ( 0 < $count && true === $all ) || ( 0 === $count && true !== $all ) ) { WP_CLI::error( 'You need to specify either one or more UUIDS or provide the --all flag' ); } if ( true === $all ) { $result = WP_Application_Passwords::delete_all_application_passwords( $user_id ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); } WP_CLI::success( 'Deleted all application passwords.' ); WP_CLI::halt( 0 ); } $errors = 0; foreach ( $args as $uuid ) { $result = WP_Application_Passwords::delete_application_password( $user_id, $uuid ); if ( $result instanceof WP_Error ) { WP_CLI::warning( "Failed to delete UUID {$uuid}: " . $result->get_error_message() ); ++$errors; } } WP_CLI::success( sprintf( 'Deleted %d of %d application %s.', $count - $errors, $count, Utils\pluralize( 'password', $count ) ) ); WP_CLI::halt( 0 === $errors ? 0 : 1 ); } /** * Checks whether an application password for a given application exists. * * ## OPTIONS * * * : The user login, user email, or user ID of the user to check the existence of an application password for. * * * : Name of the application to check the existence of an application password for. * * ## EXAMPLES * * # Check if an application password for a given application exists * $ wp user application-password exists 123 myapp * $ echo $? * 1 * * # Bash script for checking whether an application password exists and creating one if not * if ! wp user application-password exists 123 myapp; then * PASSWORD=$(wp user application-password create 123 myapp --porcelain) * fi * * @param array $args Indexed array of positional arguments. * @param array $assoc_args Associative array of associative arguments. * @throws ExitException If the application password could not be created. */ public function exists( $args, $assoc_args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $args = $this->replace_login_with_user_id( $args ); list( $user_id, $app_name ) = $args; $result = $this->application_name_exists_for_user( $user_id, $app_name ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); } WP_CLI::halt( $result ? 0 : 1 ); } /** * Replaces user_login value with user ID. * * @param array $args Associative array of arguments. * @return array Associative array of arguments with the user login replaced with an ID. * @throws ExitException If the user is not found. */ private function replace_login_with_user_id( $args ) { $user = ( new UserFetcher() )->get_check( $args[0] ); $args[0] = $user->ID; return $args; } /** * Checks if application name exists for the given user. * * This is a polyfill for WP_Application_Passwords::get_user_application_passwords(), which was only added for * WordPress 5.7+, but we're already supporting application passwords for WordPress 5.6+. * * @param int $user_id User ID to check the application passwords for. * @param string $app_name Application name to look for. * @return bool */ private function application_name_exists_for_user( $user_id, $app_name ) { if ( Utils\wp_version_compare( '5.7', '<' ) ) { $passwords = WP_Application_Passwords::get_user_application_passwords( $user_id ); foreach ( $passwords as $password ) { if ( strtolower( $password['name'] ) === strtolower( $app_name ) ) { return true; } } return false; } return WP_Application_Passwords::application_name_exists_for_user( $user_id, $app_name ); } }