[ Index ]

PHP Cross Reference of WordPress

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


Generated: Tue Jan 18 01:00:03 2022 Cross-referenced by PHPXref 0.7.1