Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-application-passwords.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
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
+
if ( ! str_starts_with( $hash, '$generic$' ) ) {
506
+
/*
507
+
* If the hash doesn't start with `$generic$`, it is a hash created with `wp_hash_password()`.
508
+
* This is the case for application passwords created before 6.8.0.
509
+
*/
510
+
return wp_check_password( $password, $hash );
511
+
}
512
+
513
+
return wp_verify_fast_hash( $password, $hash );
514
+
}
515
+
}
516
+