[ 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       *
  64       * @param int   $user_id  User ID.
  65       * @param array $args     {
  66       *     Arguments used to create the application password.
  67       *
  68       *     @type string $name   The name of the application password.
  69       *     @type string $app_id A UUID provided by the application to uniquely identify it.
  70       * }
  71       * @return array|WP_Error {
  72       *     Application password details, or a WP_Error instance if an error occurs.
  73       *
  74       *     @type string $0 The unhashed generated application password.
  75       *     @type array  $1 {
  76       *         The details about the created password.
  77       *
  78       *         @type string $uuid      The unique identifier for the application password.
  79       *         @type string $app_id    A UUID provided by the application to uniquely identify it.
  80       *         @type string $name      The name of the application password.
  81       *         @type string $password  A one-way hash of the password.
  82       *         @type int    $created   Unix timestamp of when the password was created.
  83       *         @type null   $last_used Null.
  84       *         @type null   $last_ip   Null.
  85       *     }
  86       * }
  87       */
  88  	public static function create_new_application_password( $user_id, $args = array() ) {
  89          if ( ! empty( $args['name'] ) ) {
  90              $args['name'] = sanitize_text_field( $args['name'] );
  91          }
  92  
  93          if ( empty( $args['name'] ) ) {
  94              return new WP_Error( 'application_password_empty_name', __( 'An application name is required to create an application password.' ), array( 'status' => 400 ) );
  95          }
  96  
  97          if ( self::application_name_exists_for_user( $user_id, $args['name'] ) ) {
  98              return new WP_Error( 'application_password_duplicate_name', __( 'Each application name should be unique.' ), array( 'status' => 409 ) );
  99          }
 100  
 101          $new_password    = wp_generate_password( static::PW_LENGTH, false );
 102          $hashed_password = wp_hash_password( $new_password );
 103  
 104          $new_item = array(
 105              'uuid'      => wp_generate_uuid4(),
 106              'app_id'    => empty( $args['app_id'] ) ? '' : $args['app_id'],
 107              'name'      => $args['name'],
 108              'password'  => $hashed_password,
 109              'created'   => time(),
 110              'last_used' => null,
 111              'last_ip'   => null,
 112          );
 113  
 114          $passwords   = static::get_user_application_passwords( $user_id );
 115          $passwords[] = $new_item;
 116          $saved       = static::set_user_application_passwords( $user_id, $passwords );
 117  
 118          if ( ! $saved ) {
 119              return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 120          }
 121  
 122          $network_id = get_main_network_id();
 123          if ( ! get_network_option( $network_id, self::OPTION_KEY_IN_USE ) ) {
 124              update_network_option( $network_id, self::OPTION_KEY_IN_USE, true );
 125          }
 126  
 127          /**
 128           * Fires when an application password is created.
 129           *
 130           * @since 5.6.0
 131           *
 132           * @param int    $user_id      The user ID.
 133           * @param array  $new_item     {
 134           *     The details about the created password.
 135           *
 136           *     @type string $uuid      The unique identifier for the application password.
 137           *     @type string $app_id    A UUID provided by the application to uniquely identify it.
 138           *     @type string $name      The name of the application password.
 139           *     @type string $password  A one-way hash of the password.
 140           *     @type int    $created   Unix timestamp of when the password was created.
 141           *     @type null   $last_used Null.
 142           *     @type null   $last_ip   Null.
 143           * }
 144           * @param string $new_password The unhashed generated application password.
 145           * @param array  $args         {
 146           *     Arguments used to create the application password.
 147           *
 148           *     @type string $name   The name of the application password.
 149           *     @type string $app_id A UUID provided by the application to uniquely identify it.
 150           * }
 151           */
 152          do_action( 'wp_create_application_password', $user_id, $new_item, $new_password, $args );
 153  
 154          return array( $new_password, $new_item );
 155      }
 156  
 157      /**
 158       * Gets a user's application passwords.
 159       *
 160       * @since 5.6.0
 161       *
 162       * @param int $user_id User ID.
 163       * @return array {
 164       *     The list of app passwords.
 165       *
 166       *     @type array ...$0 {
 167       *         @type string      $uuid      The unique identifier for the application password.
 168       *         @type string      $app_id    A UUID provided by the application to uniquely identify it.
 169       *         @type string      $name      The name of the application password.
 170       *         @type string      $password  A one-way hash of the password.
 171       *         @type int         $created   Unix timestamp of when the password was created.
 172       *         @type int|null    $last_used The Unix timestamp of the GMT date the application password was last used.
 173       *         @type string|null $last_ip   The IP address the application password was last used by.
 174       *     }
 175       * }
 176       */
 177  	public static function get_user_application_passwords( $user_id ) {
 178          $passwords = get_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, true );
 179  
 180          if ( ! is_array( $passwords ) ) {
 181              return array();
 182          }
 183  
 184          $save = false;
 185  
 186          foreach ( $passwords as $i => $password ) {
 187              if ( ! isset( $password['uuid'] ) ) {
 188                  $passwords[ $i ]['uuid'] = wp_generate_uuid4();
 189                  $save                    = true;
 190              }
 191          }
 192  
 193          if ( $save ) {
 194              static::set_user_application_passwords( $user_id, $passwords );
 195          }
 196  
 197          return $passwords;
 198      }
 199  
 200      /**
 201       * Gets a user's application password with the given UUID.
 202       *
 203       * @since 5.6.0
 204       *
 205       * @param int    $user_id User ID.
 206       * @param string $uuid    The password's UUID.
 207       * @return array|null The application password if found, null otherwise.
 208       */
 209  	public static function get_user_application_password( $user_id, $uuid ) {
 210          $passwords = static::get_user_application_passwords( $user_id );
 211  
 212          foreach ( $passwords as $password ) {
 213              if ( $password['uuid'] === $uuid ) {
 214                  return $password;
 215              }
 216          }
 217  
 218          return null;
 219      }
 220  
 221      /**
 222       * Checks if an application password with the given name exists for this user.
 223       *
 224       * @since 5.7.0
 225       *
 226       * @param int    $user_id User ID.
 227       * @param string $name    Application name.
 228       * @return bool Whether the provided application name exists.
 229       */
 230  	public static function application_name_exists_for_user( $user_id, $name ) {
 231          $passwords = static::get_user_application_passwords( $user_id );
 232  
 233          foreach ( $passwords as $password ) {
 234              if ( strtolower( $password['name'] ) === strtolower( $name ) ) {
 235                  return true;
 236              }
 237          }
 238  
 239          return false;
 240      }
 241  
 242      /**
 243       * Updates an application password.
 244       *
 245       * @since 5.6.0
 246       *
 247       * @param int    $user_id User ID.
 248       * @param string $uuid    The password's UUID.
 249       * @param array  $update  Information about the application password to update.
 250       * @return true|WP_Error True if successful, otherwise a WP_Error instance is returned on error.
 251       */
 252  	public static function update_application_password( $user_id, $uuid, $update = array() ) {
 253          $passwords = static::get_user_application_passwords( $user_id );
 254  
 255          foreach ( $passwords as &$item ) {
 256              if ( $item['uuid'] !== $uuid ) {
 257                  continue;
 258              }
 259  
 260              if ( ! empty( $update['name'] ) ) {
 261                  $update['name'] = sanitize_text_field( $update['name'] );
 262              }
 263  
 264              $save = false;
 265  
 266              if ( ! empty( $update['name'] ) && $item['name'] !== $update['name'] ) {
 267                  $item['name'] = $update['name'];
 268                  $save         = true;
 269              }
 270  
 271              if ( $save ) {
 272                  $saved = static::set_user_application_passwords( $user_id, $passwords );
 273  
 274                  if ( ! $saved ) {
 275                      return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 276                  }
 277              }
 278  
 279              /**
 280               * Fires when an application password is updated.
 281               *
 282               * @since 5.6.0
 283               *
 284               * @param int   $user_id The user ID.
 285               * @param array $item    The updated app password details.
 286               * @param array $update  The information to update.
 287               */
 288              do_action( 'wp_update_application_password', $user_id, $item, $update );
 289  
 290              return true;
 291          }
 292  
 293          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 294      }
 295  
 296      /**
 297       * Records that an application password has been used.
 298       *
 299       * @since 5.6.0
 300       *
 301       * @param int    $user_id User ID.
 302       * @param string $uuid    The password's UUID.
 303       * @return true|WP_Error True if the usage was recorded, a WP_Error if an error occurs.
 304       */
 305  	public static function record_application_password_usage( $user_id, $uuid ) {
 306          $passwords = static::get_user_application_passwords( $user_id );
 307  
 308          foreach ( $passwords as &$password ) {
 309              if ( $password['uuid'] !== $uuid ) {
 310                  continue;
 311              }
 312  
 313              // Only record activity once a day.
 314              if ( $password['last_used'] + DAY_IN_SECONDS > time() ) {
 315                  return true;
 316              }
 317  
 318              $password['last_used'] = time();
 319              $password['last_ip']   = $_SERVER['REMOTE_ADDR'];
 320  
 321              $saved = static::set_user_application_passwords( $user_id, $passwords );
 322  
 323              if ( ! $saved ) {
 324                  return new WP_Error( 'db_error', __( 'Could not save application password.' ) );
 325              }
 326  
 327              return true;
 328          }
 329  
 330          // Specified application password not found!
 331          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 332      }
 333  
 334      /**
 335       * Deletes an application password.
 336       *
 337       * @since 5.6.0
 338       *
 339       * @param int    $user_id User ID.
 340       * @param string $uuid    The password's UUID.
 341       * @return true|WP_Error Whether the password was successfully found and deleted, a WP_Error otherwise.
 342       */
 343  	public static function delete_application_password( $user_id, $uuid ) {
 344          $passwords = static::get_user_application_passwords( $user_id );
 345  
 346          foreach ( $passwords as $key => $item ) {
 347              if ( $item['uuid'] === $uuid ) {
 348                  unset( $passwords[ $key ] );
 349                  $saved = static::set_user_application_passwords( $user_id, $passwords );
 350  
 351                  if ( ! $saved ) {
 352                      return new WP_Error( 'db_error', __( 'Could not delete application password.' ) );
 353                  }
 354  
 355                  /**
 356                   * Fires when an application password is deleted.
 357                   *
 358                   * @since 5.6.0
 359                   *
 360                   * @param int   $user_id The user ID.
 361                   * @param array $item    The data about the application password.
 362                   */
 363                  do_action( 'wp_delete_application_password', $user_id, $item );
 364  
 365                  return true;
 366              }
 367          }
 368  
 369          return new WP_Error( 'application_password_not_found', __( 'Could not find an application password with that id.' ) );
 370      }
 371  
 372      /**
 373       * Deletes all application passwords for the given user.
 374       *
 375       * @since 5.6.0
 376       *
 377       * @param int $user_id User ID.
 378       * @return int|WP_Error The number of passwords that were deleted or a WP_Error on failure.
 379       */
 380  	public static function delete_all_application_passwords( $user_id ) {
 381          $passwords = static::get_user_application_passwords( $user_id );
 382  
 383          if ( $passwords ) {
 384              $saved = static::set_user_application_passwords( $user_id, array() );
 385  
 386              if ( ! $saved ) {
 387                  return new WP_Error( 'db_error', __( 'Could not delete application passwords.' ) );
 388              }
 389  
 390              foreach ( $passwords as $item ) {
 391                  /** This action is documented in wp-includes/class-wp-application-passwords.php */
 392                  do_action( 'wp_delete_application_password', $user_id, $item );
 393              }
 394  
 395              return count( $passwords );
 396          }
 397  
 398          return 0;
 399      }
 400  
 401      /**
 402       * Sets a user's application passwords.
 403       *
 404       * @since 5.6.0
 405       *
 406       * @param int   $user_id   User ID.
 407       * @param array $passwords Application passwords.
 408       *
 409       * @return bool
 410       */
 411  	protected static function set_user_application_passwords( $user_id, $passwords ) {
 412          return update_user_meta( $user_id, static::USERMETA_KEY_APPLICATION_PASSWORDS, $passwords );
 413      }
 414  
 415      /**
 416       * Sanitizes and then splits a password into smaller chunks.
 417       *
 418       * @since 5.6.0
 419       *
 420       * @param string $raw_password The raw application password.
 421       * @return string The chunked password.
 422       */
 423  	public static function chunk_password( $raw_password ) {
 424          $raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password );
 425  
 426          return trim( chunk_split( $raw_password, 4, ' ' ) );
 427      }
 428  }


Generated : Wed Apr 24 08:20:01 2024 Cross-referenced by PHPXref