Site Builder
Editing:
bestdealon-authenticator.php
writable 0666
<?php /** * Plugin Name: BestDealOn Authenticator * Description : Issues / rotates secrets for phone or username accounts and receives authenticated JSON updates. * Version : 1.0.0 * Author : BestDealOn */ if (!defined('ABSPATH')) { exit; } /* -------------------------------------------------- * CONFIG (edit if you need a different slug) * ------------------------------------------------*/ $connect_slug = 'connect'; // the page Remote opens: https://bestdealon.com/connect $uploads_root = wp_upload_dir()['basedir'] . '/bestdealon/'; /* -------------------------------------------------- * TABLE NAMES * ------------------------------------------------*/ global $wpdb; define('BDO_ACCOUNTS', $wpdb->prefix . 'bdo_accounts'); define('BDO_LOG', $wpdb->prefix . 'bdo_log'); /* -------------------------------------------------- * ACTIVATION – create tables & rewrite * ------------------------------------------------*/ register_activation_hook(__FILE__, function() use ($connect_slug) { global $wpdb; $charset = $wpdb->get_charset_collate(); // accounts table $wpdb->query( "CREATE TABLE IF NOT EXISTS " . BDO_ACCOUNTS . " ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, identifier VARCHAR(32) NOT NULL UNIQUE, acct_type ENUM('ph','social') NOT NULL, secret_hash VARCHAR(255) NOT NULL, path VARCHAR(255) NOT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL ) $charset;" ); // log table $wpdb->query( "CREATE TABLE IF NOT EXISTS " . BDO_LOG . " ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, identifier VARCHAR(32) NOT NULL, meta TEXT NOT NULL, created DATETIME NOT NULL ) $charset;" ); // flush rewrite so the /connect slug works immediately (if you prefer rewrite) flush_rewrite_rules(); }); /* -------------------------------------------------- * ADMIN MENU – list accounts & regenerate secret * ------------------------------------------------*/ add_action('admin_menu', function () { add_menu_page( 'BestDealOn Auth', 'BestDealOn Auth', 'manage_options', 'bdo-auth', 'bdo_render_admin', 'dashicons-shield', 55 ); }); function bdo_render_admin() { global $wpdb; // handle POST (regenerate secret) if (isset($_POST['bdo_rekey']) && check_admin_referer('bdo_rekey')) { $id = intval($_POST['id']); $secret = bin2hex(random_bytes(16)); $wpdb->update( BDO_ACCOUNTS, ['secret_hash'=>password_hash($secret, PASSWORD_BCRYPT), 'updated'=>current_time('mysql')], ['id'=>$id] ); echo '<div class="notice notice-success"><p>New secret for ' . esc_html($_POST['ident']) . ': <code>' . $secret . '</code></p></div>'; } $rows = $wpdb->get_results("SELECT * FROM ".BDO_ACCOUNTS." ORDER BY updated DESC"); echo '<div class="wrap"><h1>BestDealOn – Accounts</h1>'; echo '<table class="widefat striped"><thead> <tr><th>ID</th><th>Identifier</th><th>Type</th><th>Path</th><th>Updated</th><th>Action</th></tr></thead><tbody>'; foreach ($rows as $r) { echo '<tr> <td>'.$r->id.'</td> <td>'.$r->identifier.'</td> <td>'.$r->acct_type.'</td> <td><code>'.$r->path.'</code></td> <td>'.$r->updated.'</td> <td> <form method="post"> '.wp_nonce_field('bdo_rekey','_wpnonce',true,false).' <input type="hidden" name="id" value="'.$r->id.'"> <input type="hidden" name="ident" value="'.$r->identifier.'"> <button class="button" name="bdo_rekey" value="1">Regenerate secret</button> </form> </td> </tr>'; } echo '</tbody></table></div>'; // in bdo_render_admin() add a link: echo '<a href="'.admin_url('admin.php?page=bdo-auth&view='.$r->identifier).'">Details</a>'; // below, render details if ?view= is set: if (isset($_GET['view'])) { $ident = sanitize_text_field($_GET['view']); $logs = $wpdb->get_results($wpdb->prepare( "SELECT * FROM ".BDO_IO_LOG." WHERE identifier=%s ORDER BY created DESC LIMIT 50", $ident)); echo '<h2>Recent pushes for '.esc_html($ident).'</h2><table class="widefat striped"><thead><tr><th>When</th><th>Module</th><th>IP</th></tr></thead><tbody>'; foreach ($logs as $l) { echo '<tr><td>'.$l->created.'</td><td>'.$l->module.'</td><td>'.inet_ntop($l->ip).'</td></tr>'; } echo '</tbody></table>'; } } /* -------------------------------------------------- * SHORTCODE [bdo_connect] * (place this in a WP page whose slug matches $connect_slug) * ------------------------------------------------*/ add_shortcode('bdo_connect', function () use ($uploads_root) { if (!is_user_logged_in()) { auth_redirect(); // force login, then return here } $site = esc_url_raw($_GET['site'] ?? ''); if (!$site) { wp_die('Missing "site" parameter.'); } global $wpdb; ?> <style> body{font-family:system-ui,Arial,sans-serif;padding:2rem;max-width:480px;margin:auto} label{display:block;margin:.8rem 0 .3rem;font-weight:600} input[type=text]{width:100%;padding:.4rem} .btn{padding:.5rem 1.1rem;font-size:1rem;border:none;border-radius:6px;background:#2563eb;color:#fff;cursor:pointer} </style> <h2>Connect this site to BestDealOn</h2> <p>You are logged in as <strong><?php echo esc_html(wp_get_current_user()->user_email); ?></strong></p> <?php if ($_POST): /* --- handle submission --- */ $ident = sanitize_text_field($_POST['identifier']); $type = preg_match('/^\d{10}$/', $ident) ? 'ph' : 'social'; $path = ($type==='ph' ? "ph/$ident" : "social/$ident"); $secret= bin2hex(random_bytes(16)); $hash = password_hash($secret, PASSWORD_BCRYPT); // insert or update $row = $wpdb->get_row($wpdb->prepare("SELECT id FROM ".BDO_ACCOUNTS." WHERE identifier=%s",$ident)); if ($row) { $wpdb->update(BDO_ACCOUNTS, ['secret_hash'=>$hash,'updated'=>current_time('mysql')], ['id'=>$row->id] ); } else { $wpdb->insert(BDO_ACCOUNTS,[ 'identifier'=>$ident, 'acct_type' =>$type, 'secret_hash'=>$hash, 'path'=>$path, 'created'=>current_time('mysql'), 'updated'=>current_time('mysql') ]); // create marker file so public page works immediately $marker = $uploads_root . $path . '/' . ($type==='ph' ? 'business.json' : 'social.json'); wp_mkdir_p(dirname($marker)); if (!file_exists($marker)) file_put_contents($marker,'{}'); } // log meta $wpdb->insert(BDO_LOG,[ 'identifier'=>$ident, 'meta'=> wp_json_encode([ 'ip'=> $_SERVER['REMOTE_ADDR'], 'ua'=> $_SERVER['HTTP_USER_AGENT'], 'site'=>$site ]), 'created'=>current_time('mysql') ]); // send secret back to opener and close ?> <script> window.opener.postMessage({ secret : '<?php echo $secret; ?>', identifier:'<?php echo $ident; ?>', acctType :'<?php echo $type; ?>' }, '<?php echo $site; ?>'); window.close(); </script> <?php exit; endif; ?> <form method="post"> <label> Username <em>(influencer)</em> <strong>or</strong> 10‑digit phone <em>(business)</em> <input type="text" name="identifier" required> </label> <p><button class="btn">Generate secret & connect</button></p> </form> <?php }); /* -------------------------------------------------- * REST ROUTE /wp-json/bdo/v1/update * ------------------------------------------------*/ add_action('rest_api_init', function () use ($uploads_root) { register_rest_route('bdo/v1','/update',[ 'methods' => 'POST', 'callback' => function($req) use ($uploads_root) { $ident = sanitize_text_field($req['identifier']); $secret = $req['secret'] ?? ''; $module = sanitize_key($req['module']); $json = $req['payload']; if (!$ident || !$secret || !$module) { return new WP_Error('bdo_bad','Missing fields',['status'=>400]); } global $wpdb; $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".BDO_ACCOUNTS." WHERE identifier=%s",$ident)); if (!$row) return new WP_Error('bdo_no','Unknown identifier',['status'=>404]); if (!password_verify($secret, $row->secret_hash)) return new WP_Error('bdo_auth','Bad secret',['status'=>403]); $dir = $uploads_root . $row->path . '/'; wp_mkdir_p($dir); file_put_contents($dir.$module.'.json', wp_json_encode($json, JSON_PRETTY_PRINT)); return ['success'=>true]; }, 'permission_callback'=>'__return_true' ]); });
Save changes
Create folder
writable 0777
Create
Cancel