[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-application-passwords.php (source)

   1  <?php
   2  /**
   3   * WP_Application_Passwords class
   4   *
   5   * @package WordPress
   6   * @since   5.6.0
   7   */
   8  
   9  /**
  10   * Class for displaying, modifying, and sanitizing application passwords.
  11   *
  12   * @package WordPress
  13   */
  14  #[AllowDynamicProperties]
  15  class WP_Application_Passwords {
  16  
  17      /**
  18       * The application passwords user meta key.
  19       *
  20       * @since 5.6.0
  21       *
  22       * @var string
  23       */
  24      const USERMETA_KEY_APPLICATION_PASSWORDS = '_application_passwords';
  25  
  26      /**
  27       * The option name used to store whether application passwords are in use.
  28       *
  29       * @since 5.6.0
  30       *
  31       * @var string
  32       */
  33      const OPTION_KEY_IN_USE = 'using_application_passwords';
  34  
  35      /**
  36       * The generated application password length.
  37       *
  38       * @since 5.6.0
  39       *
  40       * @var int
  41       */
  42      const PW_LENGTH = 24;
  43  
  44      /**
  45       * Checks if application passwords are being used by the site.
  46       *
  47       * This returns true if at least one application password has ever been created.
  48       *
  49       * @since 5.6.0
  50       *
  51       * @return bool
  52       */
  53  	public static function is_in_use() {
  54          $network_id = get_main_network_id();
  55          return (bool) get_network_option( $network_id, self::OPTION_KEY_IN_USE );
  56      }
  57  
  58      /**
  59       * Creates a new application password.
  60       *
  61       * @since 5.6.0
  62       * @since 5.7.0 Returns WP_Error if application name already exists.
  63       * @since 6.8.0 The hashed password value now uses wp_fast_hash() instead of phpass.
  64       *
  65       * @param int   $user_id  User ID.
  66       * @param array $args     {
  67       *     Arguments used to create the application password.
  68       *
  69       *     @type string $name   The name of the application password.
  70       *     @type string $app_id A UUID provided by the application to uniquely identify it.
  71       * }
  72       * @return array|WP_Error {
  73       *     Application password details, or a WP_Error instance if an error occurs.
  74       *
  75       *     @type string $0 The generated application password in plain text.
  76       *     @type array  $1 {
  77       *         The details about the created password.
  78       *
  79       *         @type string $uuid      The unique identifier for the application password.
  80       *         @type string $app_id    A UUID provided by the application to uniquely identify it.
  81       *         @type string $name      The name of the application password.
  82       *         @type string $password  A one-way hash of the password.
  83       *         @type int    $created   Unix timestamp of when the password was created.
  84       *         @type null   $last_used Null.
  85       *         @type null   $last_ip   Null.
  86       *     }
  87       * }
  88       */
  89  	public static function create_new_application_password( $user_id, $args = array() ) {
  90          if ( ! empty( $args['name'] ) ) {
  91              $args['name'] = sanitize_text_field( $args['name'] );
  92          }
  93  
  94          if ( empty( $args['name'] ) ) {
  95              return new WP_Error( 'application_password_empty_name', __( 'An application name is required to create an application password.' ), array( 'status' => 400 ) );
  96          }
  97  
  98          $new_password    = wp_generate_password( static::PW_LENGTH, false );
  99          $hashed_password = self::hash_password( $new_password );
 100  
 101          $new_item = array(
 102              'uuid'      => wp_generate_uuid4(),
 103              'app_id'    => empty( $args['app_id'] ) ? '' : $args['app_id'],
 104              'name'      => $args['name'],
 105              'password'  => $hashed_password,
 106              'created'   => time(),
 107              'last_used' => null,
 108              'last_ip'   => null,
 109          );
 110  
 111          $passwords   = static::get_user_application_passwords( $user_id );
 112          $passwords[] = $new_item;
 113          $saved       = static::set_user_application_passwords( $user_id, $passwords );
 114  
 115          if ( ! $saved ) {
 116              return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 117          }
 118  
 119          $network_id = get_main_network_id();
 120          if ( ! get_network_option( $network_id, self::OPTION_KEY_IN_USE ) ) {
 121              update_network_option( $network_id, self::OPTION_KEY_IN_USE, true );
 122          }
 123  
 124          /**
 125           * Fires when an application password is created.
 126           *
 127           * @since 5.6.0
 128           * @since 6.8.0 The hashed password value now uses wp_fast_hash() instead of phpass.
 129           *
 130           * @param int    $user_id      The user ID.
 131           * @param array  $new_item     {
 132           *     The details about the created password.
 133           *
 134           *     @type string $uuid      The unique identifier for the application password.
 135           *     @type string $app_id    A UUID provided by the application to uniquely identify it.
 136           *     @type string $name      The name of the application password.
 137           *     @type string $password  A one-way hash of the password.
 138           *     @type int    $created   Unix timestamp of when the password was created.
 139           *     @type null   $last_used Null.
 140           *     @type null   $last_ip   Null.
 141           * }
 142           * @param string $new_password The generated application password in plain text.
 143           * @param array  $args         {
 144           *     Arguments used to create the application password.
 145           *
 146           *     @type string $name   The name of the application password.
 147           *     @type string $app_id A UUID provided by the application to uniquely identify it.
 148           * }
 149           */
 150          do_action( 'wp_create_application_password', $user_id, $new_item, $new_password, $args );
 151  
 152          return array( $new_password, $new_item );
 153      }
 154  
 155      /**
 156       * Gets a user's application passwords.
 157       *
 158       * @since 5.6.0
 159       *
 160       * @param int $user_id User ID.
 161       * @return array {
 162       *     The list of application passwords.
 163       *
 164       *     @type array ...$0 {
 165       *         @type string      $uuid      The unique identifier for the application password.
 166       *         @type string      $app_id    A UUID provided by the application to uniquely identify it.
 167       *         @type string      $name      The name of the application password.
 168       *         @type string      $password  A one-way hash of the password.
 169       *         @type int         $created   Unix timestamp of when the password was created.
 170       *         @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 171       *         @type string|null $last_ip   The IP address the application password was last used by.
 172       *     }
 173       * }
 174       */
 175  	public static function get_user_application_passwords( $user_id ) {
 176          $passwords = get_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, true );
 177  
 178          if ( ! is_array( $passwords ) ) {
 179              return array();
 180          }
 181  
 182          $save = false;
 183  
 184          foreach ( $passwords as $i => $password ) {
 185              if ( ! isset( $password['uuid'] ) ) {
 186                  $passwords[ $i ]['uuid'] = wp_generate_uuid4();
 187                  $save                    = true;
 188              }
 189          }
 190  
 191          if ( $save ) {
 192              static::set_user_application_passwords( $user_id, $passwords );
 193          }
 194  
 195          return $passwords;
 196      }
 197  
 198      /**
 199       * Gets a user's application password with the given UUID.
 200       *
 201       * @since 5.6.0
 202       *
 203       * @param int    $user_id User ID.
 204       * @param string $uuid    The password's UUID.
 205       * @return array|null {
 206       *     The application password if found, null otherwise.
 207       *
 208       *     @type string      $uuid      The unique identifier for the application password.
 209       *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
 210       *     @type string      $name      The name of the application password.
 211       *     @type string      $password  A one-way hash of the password.
 212       *     @type int         $created   Unix timestamp of when the password was created.
 213       *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 214       *     @type string|null $last_ip   The IP address the application password was last used by.
 215       * }
 216       */
 217  	public static function get_user_application_password( $user_id, $uuid ) {
 218          $passwords = static::get_user_application_passwords( $user_id );
 219  
 220          foreach ( $passwords as $password ) {
 221              if ( $password['uuid'] === $uuid ) {
 222                  return $password;
 223              }
 224          }
 225  
 226          return null;
 227      }
 228  
 229      /**
 230       * Checks if an application password with the given name exists for this user.
 231       *
 232       * @since 5.7.0
 233       *
 234       * @param int    $user_id User ID.
 235       * @param string $name    Application name.
 236       * @return bool Whether the provided application name exists.
 237       */
 238  	public static function application_name_exists_for_user( $user_id, $name ) {
 239          $passwords = static::get_user_application_passwords( $user_id );
 240  
 241          foreach ( $passwords as $password ) {
 242              if ( strtolower( $password['name'] ) === strtolower( $name ) ) {
 243                  return true;
 244              }
 245          }
 246  
 247          return false;
 248      }
 249  
 250      /**
 251       * Updates an application password.
 252       *
 253       * @since 5.6.0
 254       * @since 6.8.0 The actual password should now be hashed using wp_fast_hash().
 255       *
 256       * @param int    $user_id User ID.
 257       * @param string $uuid    The password's UUID.
 258       * @param array  $update  {
 259       *     Information about the application password to update.
 260       *
 261       *     @type string      $uuid      The unique identifier for the application password.
 262       *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
 263       *     @type string      $name      The name of the application password.
 264       *     @type string      $password  A one-way hash of the password.
 265       *     @type int         $created   Unix timestamp of when the password was created.
 266       *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 267       *     @type string|null $last_ip   The IP address the application password was last used by.
 268       * }
 269       * @return true|WP_Error True if successful, otherwise a WP_Error instance is returned on error.
 270       */
 271  	public static function update_application_password( $user_id, $uuid, $update = array() ) {
 272          $passwords = static::get_user_application_passwords( $user_id );
 273  
 274          foreach ( $passwords as &$item ) {
 275              if ( $item['uuid'] !== $uuid ) {
 276                  continue;
 277              }
 278  
 279              if ( ! empty( $update['name'] ) ) {
 280                  $update['name'] = sanitize_text_field( $update['name'] );
 281              }
 282  
 283              $save = false;
 284  
 285              if ( ! empty( $update['name'] ) && $item['name'] !== $update['name'] ) {
 286                  $item['name'] = $update['name'];
 287                  $save         = true;
 288              }
 289  
 290              if ( $save ) {
 291                  $saved = static::set_user_application_passwords( $user_id, $passwords );
 292  
 293                  if ( ! $saved ) {
 294                      return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 295                  }
 296              }
 297  
 298              /**
 299               * Fires when an application password is updated.
 300               *
 301               * @since 5.6.0
 302               * @since 6.8.0 The password is now hashed using wp_fast_hash() instead of phpass.
 303               *              Existing passwords may still be hashed using phpass.
 304               *
 305               * @param int   $user_id The user ID.
 306               * @param array $item    {
 307               *     The updated application password details.
 308               *
 309               *     @type string      $uuid      The unique identifier for the application password.
 310               *     @type string      $app_id    A UUID provided by the application to uniquely identify it.
 311               *     @type string      $name      The name of the application password.
 312               *     @type string      $password  A one-way hash of the password.
 313               *     @type int         $created   Unix timestamp of when the password was created.
 314               *     @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 315               *     @type string|null $last_ip   The IP address the application password was last used by.
 316               * }
 317               * @param array $update  The information to update.
 318               */
 319              do_action( 'wp_update_application_password', $user_id, $item, $update );
 320  
 321              return true;
 322          }
 323  
 324          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 325      }
 326  
 327      /**
 328       * Records that an application password has been used.
 329       *
 330       * @since 5.6.0
 331       *
 332       * @param int    $user_id User ID.
 333       * @param string $uuid    The password's UUID.
 334       * @return true|WP_Error True if the usage was recorded, a WP_Error if an error occurs.
 335       */
 336  	public static function record_application_password_usage( $user_id, $uuid ) {
 337          $passwords = static::get_user_application_passwords( $user_id );
 338  
 339          foreach ( $passwords as &$password ) {
 340              if ( $password['uuid'] !== $uuid ) {
 341                  continue;
 342              }
 343  
 344              // Only record activity once a day.
 345              if ( $password['last_used'] + DAY_IN_SECONDS > time() ) {
 346                  return true;
 347              }
 348  
 349              $password['last_used'] = time();
 350              $password['last_ip']   = $_SERVER['REMOTE_ADDR'];
 351  
 352              $saved = static::set_user_application_passwords( $user_id, $passwords );
 353  
 354              if ( ! $saved ) {
 355                  return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 356              }
 357  
 358              return true;
 359          }
 360  
 361          // Specified application password not found!
 362          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 363      }
 364  
 365      /**
 366       * Deletes an application password.
 367       *
 368       * @since 5.6.0
 369       *
 370       * @param int    $user_id User ID.
 371       * @param string $uuid    The password's UUID.
 372       * @return true|WP_Error Whether the password was successfully found and deleted, a WP_Error otherwise.
 373       */
 374  	public static function delete_application_password( $user_id, $uuid ) {
 375          $passwords = static::get_user_application_passwords( $user_id );
 376  
 377          foreach ( $passwords as $key => $item ) {
 378              if ( $item['uuid'] === $uuid ) {
 379                  unset( $passwords[ $key ] );
 380                  $saved = static::set_user_application_passwords( $user_id, $passwords );
 381  
 382                  if ( ! $saved ) {
 383                      return new WP_Error( 'db_error', __( 'Could not delete application password.' ) );
 384                  }
 385  
 386                  /**
 387                   * Fires when an application password is deleted.
 388                   *
 389                   * @since 5.6.0
 390                   *
 391                   * @param int   $user_id The user ID.
 392                   * @param array $item    The data about the application password.
 393                   */
 394                  do_action( 'wp_delete_application_password', $user_id, $item );
 395  
 396                  return true;
 397              }
 398          }
 399  
 400          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 401      }
 402  
 403      /**
 404       * Deletes all application passwords for the given user.
 405       *
 406       * @since 5.6.0
 407       *
 408       * @param int $user_id User ID.
 409       * @return int|WP_Error The number of passwords that were deleted or a WP_Error on failure.
 410       */
 411  	public static function delete_all_application_passwords( $user_id ) {
 412          $passwords = static::get_user_application_passwords( $user_id );
 413  
 414          if ( $passwords ) {
 415              $saved = static::set_user_application_passwords( $user_id, array() );
 416  
 417              if ( ! $saved ) {
 418                  return new WP_Error( 'db_error', __( 'Could not delete application passwords.' ) );
 419              }
 420  
 421              foreach ( $passwords as $item ) {
 422                  /** This action is documented in wp-includes/class-wp-application-passwords.php */
 423                  do_action( 'wp_delete_application_password', $user_id, $item );
 424              }
 425  
 426              return count( $passwords );
 427          }
 428  
 429          return 0;
 430      }
 431  
 432      /**
 433       * Sets a user's application passwords.
 434       *
 435       * @since 5.6.0
 436       *
 437       * @param int   $user_id   User ID.
 438       * @param array $passwords {
 439       *     The list of application passwords.
 440       *
 441       *     @type array ...$0 {
 442       *         @type string      $uuid      The unique identifier for the application password.
 443       *         @type string      $app_id    A UUID provided by the application to uniquely identify it.
 444       *         @type string      $name      The name of the application password.
 445       *         @type string      $password  A one-way hash of the password.
 446       *         @type int         $created   Unix timestamp of when the password was created.
 447       *         @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 448       *         @type string|null $last_ip   The IP address the application password was last used by.
 449       *     }
 450       * }
 451       * @return int|bool User meta ID if the key didn't exist (ie. this is the first time that an application password
 452       *                  has been saved for the user), true on successful update, false on failure or if the value passed
 453       *                  is the same as the one that is already in the database.
 454       */
 455  	protected static function set_user_application_passwords( $user_id, $passwords ) {
 456          return update_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, $passwords );
 457      }
 458  
 459      /**
 460       * Sanitizes and then splits a password into smaller chunks.
 461       *
 462       * @since 5.6.0
 463       *
 464       * @param string $raw_password The raw application password.
 465       * @return string The chunked password.
 466       */
 467  	public static function chunk_password(
 468          #[\SensitiveParameter]
 469          $raw_password
 470      ) {
 471          $raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password );
 472  
 473          return trim( chunk_split( $raw_password, 4, ' ' ) );
 474      }
 475  
 476      /**
 477       * Hashes a plaintext application password.
 478       *
 479       * @since 6.8.0
 480       *
 481       * @param string $password Plaintext password.
 482       * @return string Hashed password.
 483       */
 484  	public static function hash_password(
 485          #[\SensitiveParameter]
 486          string $password
 487      ): string {
 488          return wp_fast_hash( $password );
 489      }
 490  
 491      /**
 492       * Checks a plaintext application password against a hashed password.
 493       *
 494       * @since 6.8.0
 495       *
 496       * @param string $password Plaintext password.
 497       * @param string $hash     Hash of the password to check against.
 498       * @return bool Whether the password matches the hashed password.
 499       */
 500  	public static function check_password(
 501          #[\SensitiveParameter]
 502          string $password,
 503          string $hash
 504      ): bool {
 505          return wp_verify_fast_hash( $password, $hash );
 506      }
 507  }


Generated : Sat Feb 22 08:20:01 2025 Cross-referenced by PHPXref