| 1 |
<?php |
| 2 |
/** |
| 3 |
* Class SupportUser |
| 4 |
* |
| 5 |
* @package ContentControl\Vendor\TrustedLogin\Client |
| 6 |
* |
| 7 |
* @copyright 2021 Katz Web Services, Inc. |
| 8 |
* |
| 9 |
* @license GPL-2.0-or-later |
| 10 |
* Modified by code-atlantic on 18-September-2023 using Strauss. |
| 11 |
* @see https://github.com/BrianHenryIE/strauss |
| 12 |
*/ |
| 13 |
namespace ContentControl\Vendor\TrustedLogin; |
| 14 |
|
| 15 |
// Exit if accessed directly |
| 16 |
if ( ! defined( 'ABSPATH' ) ) { |
| 17 |
exit; |
| 18 |
} |
| 19 |
|
| 20 |
use \Exception; |
| 21 |
use \WP_Error; |
| 22 |
use \WP_User; |
| 23 |
use \WP_Admin_Bar; |
| 24 |
|
| 25 |
/** |
| 26 |
* The TrustedLogin all-in-one drop-in class. |
| 27 |
*/ |
| 28 |
final class SupportUser { |
| 29 |
|
| 30 |
/** |
| 31 |
* @var string The query parameter used to pass the unique user ID |
| 32 |
*/ |
| 33 |
const ID_QUERY_PARAM = 'tlid'; |
| 34 |
|
| 35 |
/** |
| 36 |
* @var Config $config |
| 37 |
*/ |
| 38 |
private $config; |
| 39 |
|
| 40 |
/** |
| 41 |
* @var Logging $logging |
| 42 |
*/ |
| 43 |
private $logging; |
| 44 |
|
| 45 |
/** |
| 46 |
* @var SupportRole $role |
| 47 |
*/ |
| 48 |
public $role; |
| 49 |
|
| 50 |
/** |
| 51 |
* @var string $user_identifier_meta_key The namespaced setting name for storing the unique identifier hash in user meta |
| 52 |
* @since 1.0.0 |
| 53 |
* @example tl_{vendor/namespace}_id |
| 54 |
*/ |
| 55 |
private $user_identifier_meta_key; |
| 56 |
|
| 57 |
/** |
| 58 |
* @var string $site_hash_meta_key The namespaced setting name for storing the site identifier hash in user meta |
| 59 |
* @since 1.0.0 |
| 60 |
* @example tl_{vendor/namespace}_site_hash |
| 61 |
*/ |
| 62 |
private $site_hash_meta_key; |
| 63 |
|
| 64 |
/** |
| 65 |
* @var int $expires_meta_key The namespaced setting name for storing the timestamp the user expires |
| 66 |
* @since 1.0.0 |
| 67 |
* @example tl_{vendor/namespace}_expires |
| 68 |
*/ |
| 69 |
private $expires_meta_key; |
| 70 |
|
| 71 |
/** |
| 72 |
* @var int $created_by_meta_key The ID of the user who created the TrustedLogin access |
| 73 |
* @since 1.0.0 |
| 74 |
*/ |
| 75 |
private $created_by_meta_key; |
| 76 |
|
| 77 |
/** |
| 78 |
* SupportUser constructor. |
| 79 |
*/ |
| 80 |
public function __construct( Config $config, Logging $logging ) { |
| 81 |
$this->config = $config; |
| 82 |
$this->logging = $logging; |
| 83 |
$this->role = new SupportRole( $config, $logging ); |
| 84 |
|
| 85 |
$this->user_identifier_meta_key = 'tl_' . $config->ns() . '_id'; |
| 86 |
$this->site_hash_meta_key = 'tl_' . $config->ns() . '_site_hash'; |
| 87 |
$this->expires_meta_key = 'tl_' . $config->ns() . '_expires'; |
| 88 |
$this->created_by_meta_key = 'tl_' . $config->ns() . '_created_by'; |
| 89 |
} |
| 90 |
|
| 91 |
/** |
| 92 |
* Allow accessing limited private properties with a magic method. |
| 93 |
* |
| 94 |
* @param string $name Name of property |
| 95 |
* |
| 96 |
* @return string|null Value of property, if defined. Otherwise, null. |
| 97 |
*/ |
| 98 |
public function __get( $name ) { |
| 99 |
|
| 100 |
// Allow accessing limited private variables |
| 101 |
switch ( $name ) { |
| 102 |
case 'identifier_meta_key': |
| 103 |
case 'expires_meta_key': |
| 104 |
case 'created_by_meta_key': |
| 105 |
return $this->{$name}; |
| 106 |
break; |
| 107 |
} |
| 108 |
|
| 109 |
return null; |
| 110 |
} |
| 111 |
|
| 112 |
/** |
| 113 |
* Checks if a Support User for this vendor has already been created. |
| 114 |
* |
| 115 |
* @since 1.0.0 |
| 116 |
* |
| 117 |
* @return int|false - WP User ID if support user exists, otherwise false. |
| 118 |
*/ |
| 119 |
public function exists() { |
| 120 |
|
| 121 |
$args = array( |
| 122 |
'number' => 1, |
| 123 |
'meta_key' => $this->user_identifier_meta_key, |
| 124 |
'meta_value' => '', |
| 125 |
'meta_compare' => 'EXISTS', |
| 126 |
'fields' => 'ID', |
| 127 |
); |
| 128 |
|
| 129 |
$user_ids = get_users( $args ); |
| 130 |
|
| 131 |
return empty( $user_ids ) ? false : (int) $user_ids[0]; |
| 132 |
} |
| 133 |
|
| 134 |
/** |
| 135 |
* Returns whether the support user exists and has an expiration time in the future. |
| 136 |
* |
| 137 |
* @since 1.0.2 |
| 138 |
* |
| 139 |
* @return bool True: Support user exists and has an expiration time in the future. False: Any of those things aren't true. |
| 140 |
*/ |
| 141 |
public function is_active( $passed_user = null ) { |
| 142 |
|
| 143 |
$current_user = is_a( $passed_user, '\WP_User' ) ? $passed_user : wp_get_current_user(); |
| 144 |
|
| 145 |
if ( ! $current_user || ! $current_user->exists() ) { |
| 146 |
return false; |
| 147 |
} |
| 148 |
|
| 149 |
$expiration = $this->get_expiration( $current_user, false, true ); |
| 150 |
|
| 151 |
if ( ! $expiration ) { |
| 152 |
return false; |
| 153 |
} |
| 154 |
|
| 155 |
if ( time() > (int) $expiration ) { |
| 156 |
return false; |
| 157 |
} |
| 158 |
|
| 159 |
return true; |
| 160 |
} |
| 161 |
|
| 162 |
/** |
| 163 |
* Create the Support User. |
| 164 |
* |
| 165 |
* @since 1.0.0 |
| 166 |
* |
| 167 |
* @uses wp_insert_user() |
| 168 |
* |
| 169 |
* @return int|WP_Error - Array with login response information if created, or WP_Error object if there was an issue. |
| 170 |
*/ |
| 171 |
public function create() { |
| 172 |
|
| 173 |
$user_id = $this->exists(); |
| 174 |
|
| 175 |
// Double-check that a user doesn't exist before trying to create a new one. |
| 176 |
if ( $user_id ) { |
| 177 |
$this->logging->log( 'Support User not created; already exists: User #' . $user_id, __METHOD__, 'notice' ); |
| 178 |
|
| 179 |
return new \WP_Error( 'user_exists', sprintf( 'A user with the User ID %d already exists', $user_id ) ); |
| 180 |
} |
| 181 |
|
| 182 |
$role = $this->role->get(); |
| 183 |
|
| 184 |
if ( is_wp_error( $role ) ) { |
| 185 |
return $role; |
| 186 |
} |
| 187 |
|
| 188 |
$user_email = $this->config->get_setting( 'vendor/email' ); |
| 189 |
$allow_existing_user_match = false; // Fail if the user already exists and the email is unhashed. |
| 190 |
|
| 191 |
if ( defined( 'LOGGED_IN_KEY' ) && defined( 'NONCE_KEY' ) ) { |
| 192 |
// The hash doesn't need to be secure, just persistent. |
| 193 |
$user_email = str_replace( '{hash}', sha1( LOGGED_IN_KEY . NONCE_KEY . get_current_blog_id() ), $user_email ); |
| 194 |
$allow_existing_user_match = true; // Don't fail if the user already exists and the email matches the hash. |
| 195 |
} |
| 196 |
|
| 197 |
$user_id_of_email = email_exists( $user_email ); |
| 198 |
|
| 199 |
if ( $user_id_of_email ) { |
| 200 |
$this->logging->log( 'Support User not created; a user with that email already exists: ' . $user_email, __METHOD__, 'warning' ); |
| 201 |
|
| 202 |
// Only allow the user to be created if the email is not hashed; that way, it's not possible to accidentally |
| 203 |
// create a user with the same email as an existing user. |
| 204 |
if ( ! $allow_existing_user_match ) { |
| 205 |
return new \WP_Error( 'email_exists', esc_html__( 'User not created; User with that email already exists', 'trustedlogin' ) ); |
| 206 |
} |
| 207 |
|
| 208 |
// If the user already exists and the email matches the hash, use that user. |
| 209 |
return $user_id_of_email; |
| 210 |
} |
| 211 |
|
| 212 |
$user_data = array( |
| 213 |
'user_login' => $this->generate_unique_username(), |
| 214 |
'user_email' => $user_email, |
| 215 |
'user_pass' => Encryption::get_random_hash( $this->logging ), |
| 216 |
'role' => $role->name, |
| 217 |
'display_name' => $this->config->get_setting( 'vendor/display_name', '' ), |
| 218 |
'user_registered' => date( 'Y-m-d H:i:s', time() ), |
| 219 |
); |
| 220 |
|
| 221 |
$new_user_id = wp_insert_user( $user_data ); |
| 222 |
|
| 223 |
if ( is_wp_error( $new_user_id ) ) { |
| 224 |
$this->logging->log( 'Error: User not created because: ' . $new_user_id->get_error_message(), __METHOD__, 'error' ); |
| 225 |
|
| 226 |
return $new_user_id; |
| 227 |
} |
| 228 |
|
| 229 |
$this->logging->log( 'Support User #' . $new_user_id, __METHOD__, 'info' ); |
| 230 |
|
| 231 |
return $new_user_id; |
| 232 |
} |
| 233 |
|
| 234 |
/** |
| 235 |
* Always return a unique username |
| 236 |
* |
| 237 |
* @return string Username, with possible number trailing, if clashes exist. |
| 238 |
*/ |
| 239 |
private function generate_unique_username() { |
| 240 |
|
| 241 |
// translators: %s is replaced with the name of the software developer (e.g. "Acme Widgets") |
| 242 |
$username = sprintf( esc_html__( '%s Support', 'trustedlogin' ), $this->config->get_setting( 'vendor/title' ) ); |
| 243 |
|
| 244 |
if ( ! username_exists( $username ) ) { |
| 245 |
return $username; |
| 246 |
} |
| 247 |
|
| 248 |
$i = 1; |
| 249 |
$new_username = $username; |
| 250 |
while ( username_exists( $new_username ) ) { |
| 251 |
$new_username = sprintf( '%s %d', $username, $i + 1 ); |
| 252 |
} |
| 253 |
|
| 254 |
return $new_username; |
| 255 |
} |
| 256 |
|
| 257 |
/** |
| 258 |
* Returns the site secret ID connected to the support user. |
| 259 |
* |
| 260 |
* @param string $user_identifier |
| 261 |
* |
| 262 |
* @return string|WP_Error|null Returns the secret ID. WP_Error if there was a problem generating any hashes. Null: No users were found using that user identifier. |
| 263 |
*/ |
| 264 |
public function get_secret_id( $user_identifier ) { |
| 265 |
|
| 266 |
$user = $this->get( $user_identifier ); |
| 267 |
|
| 268 |
if ( is_null( $user ) ) { |
| 269 |
return null; |
| 270 |
} |
| 271 |
|
| 272 |
$site_identifier_hash = $this->get_site_hash( $user ); |
| 273 |
|
| 274 |
if ( is_wp_error( $site_identifier_hash ) ) { |
| 275 |
return $site_identifier_hash; |
| 276 |
} |
| 277 |
|
| 278 |
$Endpoint = new Endpoint( $this->config, $this->logging ); |
| 279 |
|
| 280 |
return $Endpoint->generate_secret_id( $site_identifier_hash ); |
| 281 |
} |
| 282 |
|
| 283 |
/** |
| 284 |
* Logs in a support user, if any exist at $user_identifier and haven't expired yet |
| 285 |
* |
| 286 |
* If the user access has expired, deletes the user with {@see SupportUser::delete()} |
| 287 |
* |
| 288 |
* @param string $user_identifier Unique identifier for support user before being hashed. |
| 289 |
* |
| 290 |
* @return true|WP_Error |
| 291 |
*/ |
| 292 |
public function maybe_login( $user_identifier ) { |
| 293 |
|
| 294 |
$support_user = $this->get( $user_identifier ); |
| 295 |
|
| 296 |
if ( empty( $support_user ) ) { |
| 297 |
|
| 298 |
$this->logging->log( 'Support user not found at identifier ' . esc_attr( $user_identifier ), __METHOD__, 'notice' ); |
| 299 |
|
| 300 |
return new \WP_Error( 'user_not_found', sprintf( 'Support user not found at identifier %s.', esc_attr( $user_identifier ) ) ); |
| 301 |
} |
| 302 |
|
| 303 |
$is_active = $this->is_active( $support_user ); |
| 304 |
|
| 305 |
// This user has expired, but the cron didn't run... |
| 306 |
if ( ! $is_active ) { |
| 307 |
|
| 308 |
$expires = $this->get_expiration( $support_user, false, true ); |
| 309 |
|
| 310 |
$this->logging->log( 'The user was supposed to expire on ' . $expires . '; revoking now.', __METHOD__, 'warning' ); |
| 311 |
|
| 312 |
$this->delete( $user_identifier, true, true ); |
| 313 |
|
| 314 |
return new \WP_Error( 'access_expired', 'The user was supposed to expire on ' . $expires . '; revoking now.' ); |
| 315 |
} |
| 316 |
|
| 317 |
$this->login( $support_user ); |
| 318 |
|
| 319 |
return true; |
| 320 |
} |
| 321 |
|
| 322 |
/** |
| 323 |
* Processes login (with extra logging) and triggers the 'trustedlogin/{ns}/login' hook |
| 324 |
* |
| 325 |
* @param \WP_User $support_user |
| 326 |
*/ |
| 327 |
private function login( \WP_User $support_user ) { |
| 328 |
|
| 329 |
if ( ! $support_user->exists() ) { |
| 330 |
|
| 331 |
$this->logging->log( sprintf( 'Login failed: Support User #%d does not exist.', $support_user->ID ), __METHOD__, 'error' ); |
| 332 |
|
| 333 |
return; |
| 334 |
} |
| 335 |
|
| 336 |
wp_set_current_user( $support_user->ID, $support_user->user_login ); |
| 337 |
wp_set_auth_cookie( $support_user->ID ); |
| 338 |
|
| 339 |
do_action( 'wp_login', $support_user->user_login, $support_user ); |
| 340 |
|
| 341 |
$this->logging->log( sprintf( 'Support User #%d logged in', $support_user->ID ), __METHOD__, 'notice' ); |
| 342 |
|
| 343 |
/** |
| 344 |
* Action run when TrustedLogin has logged-in |
| 345 |
*/ |
| 346 |
do_action( 'trustedlogin/' . $this->config->ns() . '/logged_in', array( |
| 347 |
'url' => get_site_url(), |
| 348 |
'action' => 'logged_in', |
| 349 |
) ); |
| 350 |
} |
| 351 |
|
| 352 |
/** |
| 353 |
* Helper Function: Get the generated support user(s). |
| 354 |
* |
| 355 |
* @since 1.0.0 |
| 356 |
* |
| 357 |
* @param string $user_identifier_or_hash |
| 358 |
* |
| 359 |
* @return \WP_User|null WP_User if found; null if not |
| 360 |
*/ |
| 361 |
public function get( $user_identifier_or_hash = '' ) { |
| 362 |
|
| 363 |
if ( empty( $user_identifier_or_hash ) ) { |
| 364 |
return null; |
| 365 |
} |
| 366 |
|
| 367 |
$user_identifier_hash = $user_identifier_or_hash; |
| 368 |
|
| 369 |
// When passed in the endpoint URL, the unique ID will be the raw value, not the hash. |
| 370 |
if ( strlen( $user_identifier_or_hash ) > 32 ) { |
| 371 |
$user_identifier_hash = Encryption::hash( $user_identifier_or_hash ); |
| 372 |
} |
| 373 |
|
| 374 |
$args = array( |
| 375 |
'number' => 1, |
| 376 |
'meta_key' => $this->user_identifier_meta_key, |
| 377 |
'meta_value' => $user_identifier_hash, |
| 378 |
); |
| 379 |
|
| 380 |
$user = get_users( $args ); |
| 381 |
|
| 382 |
return empty( $user ) ? null : $user[0]; |
| 383 |
} |
| 384 |
|
| 385 |
/** |
| 386 |
* Returns the expiration for user access as either a human-readable string or timestamp. |
| 387 |
* |
| 388 |
* @param \WP_User $user |
| 389 |
* @param bool $human_readable Whether to show expiration as a human_time_diff()-formatted string. Default: false. |
| 390 |
* @param bool $gmt Whether to use GMT timestamp in the human-readable result. Not used if $human_readable is false. Default: false. |
| 391 |
* |
| 392 |
* @return int|string|false False if no expiration is set. Expiration timestamp if $human_readable is false. Time diff if $human_readable is true. |
| 393 |
*/ |
| 394 |
public function get_expiration( \WP_User $user, $human_readable = false, $gmt = false ) { |
| 395 |
|
| 396 |
$expiration = get_user_option( $this->expires_meta_key, $user->ID ); |
| 397 |
|
| 398 |
if ( ! $expiration ) { |
| 399 |
return false; |
| 400 |
} |
| 401 |
|
| 402 |
return $human_readable ? human_time_diff( current_time( 'timestamp', $gmt ), $expiration ) : $expiration; |
| 403 |
} |
| 404 |
|
| 405 |
/** |
| 406 |
* Get all users with the support role. |
| 407 |
* |
| 408 |
* @since 1.0.0 |
| 409 |
* |
| 410 |
* @return \WP_User[] |
| 411 |
*/ |
| 412 |
public function get_all() { |
| 413 |
|
| 414 |
static $support_users = null; |
| 415 |
|
| 416 |
// Only fetch once per process |
| 417 |
if ( ! is_null( $support_users ) ) { |
| 418 |
return $support_users; |
| 419 |
} |
| 420 |
|
| 421 |
$args = array( |
| 422 |
'number' => - 1, |
| 423 |
'meta_key' => $this->user_identifier_meta_key, |
| 424 |
'meta_compare' => 'EXISTS', |
| 425 |
'meta_value' => '', |
| 426 |
); |
| 427 |
|
| 428 |
$support_users = get_users( $args ); |
| 429 |
|
| 430 |
return $support_users; |
| 431 |
} |
| 432 |
|
| 433 |
|
| 434 |
/** |
| 435 |
* Returns the first support user active on the site, if any. |
| 436 |
* |
| 437 |
* @since 1.0.0 |
| 438 |
* |
| 439 |
* @return \WP_User|null |
| 440 |
*/ |
| 441 |
public function get_first() { |
| 442 |
$support_users = $this->get_all(); |
| 443 |
|
| 444 |
if ( $support_users ) { |
| 445 |
return $support_users[0]; |
| 446 |
} |
| 447 |
|
| 448 |
return null; |
| 449 |
} |
| 450 |
|
| 451 |
/** |
| 452 |
* Deletes support user(s) with options to delete the TrustedLogin-created user role and endpoint as well |
| 453 |
* |
| 454 |
* @used-by SupportUser::maybe_login() Called when user access has expired, but the cron didn't run... |
| 455 |
* @used-by Client::revoke_access() |
| 456 |
* |
| 457 |
* @param string $user_identifier Unique identifier of the user to delete. |
| 458 |
* @param bool $delete_role Should the TrustedLogin-created user role be deleted also? Default: `true`. |
| 459 |
* @param bool $delete_endpoint Should the TrustedLogin endpoint for the site be deleted also? Default: `true`. |
| 460 |
* |
| 461 |
* @return bool|WP_Error True: Successfully removed user and role; false: There are no support users matching $user_identifier; WP_Error: something went wrong. |
| 462 |
*/ |
| 463 |
public function delete( $user_identifier = '', $delete_role = true, $delete_endpoint = true ) { |
| 464 |
|
| 465 |
require_once ABSPATH . 'wp-admin/includes/user.php'; // Needed for wp_delete_user() |
| 466 |
|
| 467 |
$user = $this->get( $user_identifier ); |
| 468 |
|
| 469 |
if ( empty( $user ) ) { |
| 470 |
return false; |
| 471 |
} |
| 472 |
|
| 473 |
$reassign_id_or_null = $this->get_reassign_user_id(); |
| 474 |
|
| 475 |
$this->logging->log( 'Processing user ID ' . $user->ID, __METHOD__, 'debug' ); |
| 476 |
|
| 477 |
// Remove auto-cleanup hook |
| 478 |
wp_clear_scheduled_hook( 'trustedlogin/' . $this->config->ns() . '/access/revoke', array( $user_identifier ) ); |
| 479 |
|
| 480 |
// Delete first using wp_delete_user() to allow for reassignment of posts |
| 481 |
$deleted = wp_delete_user( $user->ID, $reassign_id_or_null ); |
| 482 |
|
| 483 |
// Also delete the user from the all sites on the WP Multisite network |
| 484 |
$wpmu_deleted = \function_exists( 'wpmu_delete_user' ) ? wpmu_delete_user( $user->ID ) : false; |
| 485 |
|
| 486 |
if ( $deleted ) { |
| 487 |
$message = 'User: ' . $user->ID . ' deleted.'; |
| 488 |
|
| 489 |
if ( $wpmu_deleted ) { |
| 490 |
$message .= ' Also deleted from the Multisite network.'; |
| 491 |
} |
| 492 |
|
| 493 |
$this->logging->log( $message, __METHOD__, 'info' ); |
| 494 |
} else { |
| 495 |
$this->logging->log( 'User: ' . $user->ID . ' was NOT deleted.', __METHOD__, 'error' ); |
| 496 |
} |
| 497 |
|
| 498 |
if ( $delete_role ) { |
| 499 |
$this->role->delete(); |
| 500 |
} |
| 501 |
|
| 502 |
if ( $delete_endpoint ) { |
| 503 |
$Endpoint = new Endpoint( $this->config, $this->logging ); |
| 504 |
$Endpoint->delete(); |
| 505 |
} |
| 506 |
|
| 507 |
// Re-run to make sure there were no race conditions |
| 508 |
return $this->delete( $user_identifier ); |
| 509 |
} |
| 510 |
|
| 511 |
/** |
| 512 |
* Get the ID of the best-guess appropriate admin user |
| 513 |
* |
| 514 |
* @since 1.0.0 |
| 515 |
* |
| 516 |
* @return int|null User ID if there are admins, null if not |
| 517 |
*/ |
| 518 |
private function get_reassign_user_id() { |
| 519 |
|
| 520 |
if ( ! $this->config->get_setting( 'reassign_posts' ) ) { |
| 521 |
return null; |
| 522 |
} |
| 523 |
|
| 524 |
// TODO: Add a filter to modify who gets auto-reassigned |
| 525 |
$admins = get_users( array( |
| 526 |
'role' => 'administrator', |
| 527 |
'orderby' => 'registered', |
| 528 |
'order' => 'DESC', |
| 529 |
'number' => 1, |
| 530 |
) ); |
| 531 |
|
| 532 |
$reassign_id = empty( $admins ) ? null : $admins[0]->ID; |
| 533 |
|
| 534 |
$this->logging->log( 'Reassign user ID: ' . var_export( $reassign_id, true ), __METHOD__, 'info' ); |
| 535 |
|
| 536 |
return $reassign_id; |
| 537 |
} |
| 538 |
|
| 539 |
/** |
| 540 |
* Schedules cron job to auto-revoke, adds user meta with unique ids |
| 541 |
* |
| 542 |
* @param int $user_id ID of generated support user |
| 543 |
* @param string $site_identifier_hash |
| 544 |
* @param int $decay_timestamp Timestamp when user will be removed |
| 545 |
* |
| 546 |
* @return string|WP_Error Value of $identifier_meta_key if worked; empty string or WP_Error if not. |
| 547 |
*/ |
| 548 |
public function setup( $user_id, $site_identifier_hash, $expiration_timestamp = null, Cron $cron = null ) { |
| 549 |
|
| 550 |
if ( $expiration_timestamp ) { |
| 551 |
|
| 552 |
$scheduled = $cron->schedule( $expiration_timestamp, $site_identifier_hash ); |
| 553 |
|
| 554 |
if ( $scheduled ) { |
| 555 |
update_user_option( $user_id, $this->expires_meta_key, $expiration_timestamp ); |
| 556 |
} |
| 557 |
} |
| 558 |
|
| 559 |
$user_identifier = Encryption::hash( $site_identifier_hash ); |
| 560 |
|
| 561 |
if ( is_wp_error( $user_identifier ) ) { |
| 562 |
return $user_identifier; |
| 563 |
} |
| 564 |
|
| 565 |
update_user_option( $user_id, $this->site_hash_meta_key, $site_identifier_hash, true ); |
| 566 |
update_user_option( $user_id, $this->user_identifier_meta_key, $user_identifier, true ); |
| 567 |
update_user_option( $user_id, $this->created_by_meta_key, get_current_user_id() ); |
| 568 |
|
| 569 |
// Make extra sure that the identifier was saved. Otherwise, things won't work! |
| 570 |
return get_user_option( $this->user_identifier_meta_key, $user_id ); |
| 571 |
} |
| 572 |
|
| 573 |
/** |
| 574 |
* Updates the scheduled cron job to auto-revoke and updates the Support User's meta. |
| 575 |
* |
| 576 |
* @param int $user_id ID of generated support user. |
| 577 |
* @param string $site_identifier_hash The unique identifier for the WP_User created {@see Encryption::get_random_hash()} |
| 578 |
* @param int $expiration_timestamp Timestamp when user will be removed. Throws error if null/empty. |
| 579 |
* @param Cron|null $cron Optional. The Cron object for handling scheduling. Defaults to null. |
| 580 |
* |
| 581 |
* @return string|WP_Error Value of $identifier_meta_key if worked; empty string or WP_Error if not. |
| 582 |
*/ |
| 583 |
public function extend( $user_id, $site_identifier_hash, $expiration_timestamp = null, $cron = null ) { |
| 584 |
|
| 585 |
if ( ! $user_id || ! $site_identifier_hash || ! $expiration_timestamp ) { |
| 586 |
return new \WP_Error( 'missing_action_parameter', 'Error extending Support User access, missing required parameter.' ); |
| 587 |
} |
| 588 |
|
| 589 |
if ( ! $cron instanceof Cron ) { |
| 590 |
// Avoid a Fatal error if `$cron` parameter is not provided. |
| 591 |
$cron = new Cron( $this->config, $this->logging ); |
| 592 |
} |
| 593 |
|
| 594 |
$rescheduled = $cron->reschedule( $expiration_timestamp, $site_identifier_hash ); |
| 595 |
|
| 596 |
if ( $rescheduled ) { |
| 597 |
update_user_option( $user_id, $this->expires_meta_key, $expiration_timestamp ); |
| 598 |
|
| 599 |
return true; |
| 600 |
} |
| 601 |
|
| 602 |
return new \WP_Error( 'extend_failed', 'Error rescheduling cron task' ); |
| 603 |
|
| 604 |
} |
| 605 |
|
| 606 |
/** |
| 607 |
* @param \WP_User|int $user_id_or_object User ID or User object |
| 608 |
* |
| 609 |
* @return string|WP_Error User unique identifier if success; WP_Error if $user is not int or WP_User. |
| 610 |
*/ |
| 611 |
public function get_user_identifier( $user_id_or_object ) { |
| 612 |
|
| 613 |
if ( empty( $this->user_identifier_meta_key ) ) { |
| 614 |
$this->logging->log( 'The meta key to identify users is not set.', __METHOD__, 'error' ); |
| 615 |
|
| 616 |
return new \WP_Error( 'missing_meta_key', 'The SupportUser object has not been properly instantiated.' ); |
| 617 |
} |
| 618 |
|
| 619 |
if ( $user_id_or_object instanceof \WP_User ) { |
| 620 |
$user_id = $user_id_or_object->ID; |
| 621 |
} elseif ( is_int( $user_id_or_object ) ) { |
| 622 |
$user_id = $user_id_or_object; |
| 623 |
} else { |
| 624 |
|
| 625 |
$this->logging->log( 'The $user_id_or_object value must be int or WP_User: ' . var_export( $user_id_or_object, true ), __METHOD__, 'error' ); |
| 626 |
|
| 627 |
return new \WP_Error( 'invalid_type', '$user must be int or WP_User' ); |
| 628 |
} |
| 629 |
|
| 630 |
return get_user_option( $this->user_identifier_meta_key, $user_id ); |
| 631 |
} |
| 632 |
|
| 633 |
/** |
| 634 |
* @param WP_User|int $user_id_or_object User ID or User object |
| 635 |
* |
| 636 |
* @return string|WP_Error User unique identifier if success; WP_Error if $user is not int or WP_User. |
| 637 |
*/ |
| 638 |
public function get_site_hash( $user_id_or_object ) { |
| 639 |
|
| 640 |
if ( empty( $this->site_hash_meta_key ) ) { |
| 641 |
$this->logging->log( 'The constructor has not been properly instantiated; the site_hash_meta_key property is not set.', __METHOD__, 'error' ); |
| 642 |
|
| 643 |
return new \WP_Error( 'missing_meta_key', 'The SupportUser object has not been properly instantiated.' ); |
| 644 |
} |
| 645 |
|
| 646 |
if ( $user_id_or_object instanceof \WP_User ) { |
| 647 |
$user_id = $user_id_or_object->ID; |
| 648 |
} elseif ( is_int( $user_id_or_object ) ) { |
| 649 |
$user_id = $user_id_or_object; |
| 650 |
} else { |
| 651 |
|
| 652 |
$this->logging->log( 'The $user_id_or_object value must be int or WP_User: ' . var_export( $user_id_or_object, true ), __METHOD__, 'error' ); |
| 653 |
|
| 654 |
return new \WP_Error( 'invalid_type', '$user must be int or WP_User' ); |
| 655 |
} |
| 656 |
|
| 657 |
return get_user_option( $this->site_hash_meta_key, $user_id ); |
| 658 |
} |
| 659 |
|
| 660 |
/** |
| 661 |
* Returns admin URL to revoke support user |
| 662 |
* |
| 663 |
* @uses SupportUser::get_user_identifier() |
| 664 |
* |
| 665 |
* @since 1.1 Removed second parameter $current_url. |
| 666 |
* |
| 667 |
* @param \WP_User|int|string $user User object, user ID, or "all". If "all", will revoke all users. |
| 668 |
* |
| 669 |
* @return string|false Unsanitized nonce URL to revoke support user. If not able to retrieve user identifier, returns false. |
| 670 |
*/ |
| 671 |
public function get_revoke_url( $user ) { |
| 672 |
|
| 673 |
// If "all", will revoke all support users. |
| 674 |
if ( 'all' === $user ) { |
| 675 |
$user_identifier = 'all'; |
| 676 |
} else { |
| 677 |
$user_identifier = $this->get_user_identifier( $user ); |
| 678 |
} |
| 679 |
|
| 680 |
if ( ! $user_identifier || is_wp_error( $user_identifier ) ) { |
| 681 |
return false; |
| 682 |
} |
| 683 |
|
| 684 |
$revoke_url = add_query_arg( array( |
| 685 |
Endpoint::REVOKE_SUPPORT_QUERY_PARAM => $this->config->ns(), |
| 686 |
self::ID_QUERY_PARAM => $user_identifier, |
| 687 |
'_wpnonce' => wp_create_nonce( Endpoint::REVOKE_SUPPORT_QUERY_PARAM ), |
| 688 |
), admin_url() ); |
| 689 |
|
| 690 |
$this->logging->log( "revoke_url: $revoke_url", __METHOD__, 'debug' ); |
| 691 |
|
| 692 |
return $revoke_url; |
| 693 |
} |
| 694 |
} |
| 695 |
|