Changeset 3442344
- Timestamp:
- 01/19/2026 09:41:11 AM (7 weeks ago)
- Location:
- basecloud-shield
- Files:
-
- 6 edited
- 1 copied
-
tags/1.2.0 (copied) (copied from basecloud-shield/trunk)
-
tags/1.2.0/basecloud-shield.php (modified) (12 diffs)
-
tags/1.2.0/package.json (modified) (2 diffs)
-
tags/1.2.0/readme.txt (modified) (5 diffs)
-
trunk/basecloud-shield.php (modified) (12 diffs)
-
trunk/package.json (modified) (2 diffs)
-
trunk/readme.txt (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
basecloud-shield/tags/1.2.0/basecloud-shield.php
r3442333 r3442344 2 2 /** 3 3 * Plugin Name: BaseCloud Shield 4 * Description: Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, and SendGrid API.5 * Version: 1. 0.14 * Description: Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, SendGrid, WhatsApp, SMS, and Webhooks. 5 * Version: 1.2.0 6 6 * Author: BaseCloud Team 7 7 * Author URI: https://www.basecloudglobal.com/ … … 15 15 if (!defined('ABSPATH')) { exit; } 16 16 17 define('BCSHIELD_VERSION', '1. 0.1');17 define('BCSHIELD_VERSION', '1.2.0'); 18 18 19 19 class BaseCloudShield { … … 61 61 $clean = []; 62 62 $clean['enable_2fa'] = !empty($input['enable_2fa']) ? 1 : 0; 63 $clean['delivery_method'] = sanitize_text_field($input['delivery_method'] ?? 'email'); 64 65 // Central Manager Email (The "Master Key" feature) 63 64 // Multiple delivery methods 65 $clean['delivery_methods'] = !empty($input['delivery_methods']) && is_array($input['delivery_methods']) 66 ? array_map('sanitize_text_field', $input['delivery_methods']) 67 : array('email'); 68 69 // User selection for OTP recipients 70 $clean['recipient_mode'] = sanitize_text_field($input['recipient_mode'] ?? 'user'); 66 71 $clean['manager_email'] = sanitize_email($input['manager_email'] ?? ''); 67 72 $clean['selected_users'] = !empty($input['selected_users']) && is_array($input['selected_users']) 73 ? array_map('intval', $input['selected_users']) 74 : array(); 75 76 // Email settings 77 $clean['from_email'] = sanitize_email($input['from_email'] ?? get_bloginfo('admin_email')); 78 79 // SendGrid settings 80 $clean['sendgrid_key'] = sanitize_text_field($input['sendgrid_key'] ?? ''); 81 82 // Webhook settings 68 83 $clean['webhook_url'] = esc_url_raw($input['webhook_url'] ?? ''); 69 $clean['sendgrid_key'] = sanitize_text_field($input['sendgrid_key'] ?? ''); 70 $clean['from_email'] = sanitize_email($input['from_email'] ?? get_bloginfo('admin_email')); 84 85 // WhatsApp settings 86 $clean['whatsapp_provider'] = sanitize_text_field($input['whatsapp_provider'] ?? 'twilio'); 87 $clean['whatsapp_account_sid'] = sanitize_text_field($input['whatsapp_account_sid'] ?? ''); 88 $clean['whatsapp_auth_token'] = sanitize_text_field($input['whatsapp_auth_token'] ?? ''); 89 $clean['whatsapp_from'] = sanitize_text_field($input['whatsapp_from'] ?? ''); 90 91 // SMS settings 92 $clean['sms_provider'] = sanitize_text_field($input['sms_provider'] ?? 'twilio'); 93 $clean['sms_account_sid'] = sanitize_text_field($input['sms_account_sid'] ?? ''); 94 $clean['sms_auth_token'] = sanitize_text_field($input['sms_auth_token'] ?? ''); 95 $clean['sms_from'] = sanitize_text_field($input['sms_from'] ?? ''); 96 71 97 $clean['otp_validity'] = max(1, min(30, intval($input['otp_validity'] ?? 10))); 72 98 return $clean; … … 88 114 set_transient('bcshield_otp_' . $user->ID, $otp, $validity_min * 60); 89 115 90 // ROUTING LOGIC: Determine Recipient 91 // If manager_email is set, send there. Otherwise, send to user. 92 $target_email = !empty($opts['manager_email']) ? $opts['manager_email'] : $user->user_email; 93 94 // DELIVERY ROUTER 95 $method = $opts['delivery_method'] ?? 'email'; 116 // ROUTING LOGIC: Determine Recipients 117 $recipients = $this->get_otp_recipients($user, $opts); 118 119 // DELIVERY ROUTER - Send via all selected methods 120 $delivery_methods = isset($opts['delivery_methods']) ? $opts['delivery_methods'] : array('email'); 96 121 $from_email = $opts['from_email'] ?? get_bloginfo('admin_email'); 97 122 98 if ($method === 'webhook' && !empty($opts['webhook_url'])) { 99 $this->send_via_webhook($user, $otp, $opts['webhook_url']); 100 } 101 elseif ($method === 'sendgrid' && !empty($opts['sendgrid_key'])) { 102 $this->send_via_sendgrid($user, $otp, $target_email, $opts['sendgrid_key'], $from_email); 103 } 104 else { 105 $this->send_via_email($user, $otp, $target_email, $from_email); 123 foreach ($delivery_methods as $method) { 124 switch ($method) { 125 case 'email': 126 foreach ($recipients as $recipient) { 127 $this->send_via_email($user, $otp, $recipient['email'], $from_email); 128 } 129 break; 130 131 case 'sendgrid': 132 if (!empty($opts['sendgrid_key'])) { 133 foreach ($recipients as $recipient) { 134 $this->send_via_sendgrid($user, $otp, $recipient['email'], $opts['sendgrid_key'], $from_email); 135 } 136 } 137 break; 138 139 case 'webhook': 140 if (!empty($opts['webhook_url'])) { 141 $this->send_via_webhook($user, $otp, $opts['webhook_url'], $recipients); 142 } 143 break; 144 145 case 'whatsapp': 146 if (!empty($opts['whatsapp_account_sid']) && !empty($opts['whatsapp_auth_token'])) { 147 foreach ($recipients as $recipient) { 148 if (!empty($recipient['phone'])) { 149 $this->send_via_whatsapp($user, $otp, $recipient['phone'], $opts); 150 } 151 } 152 } 153 break; 154 155 case 'sms': 156 if (!empty($opts['sms_account_sid']) && !empty($opts['sms_auth_token'])) { 157 foreach ($recipients as $recipient) { 158 if (!empty($recipient['phone'])) { 159 $this->send_via_sms($user, $otp, $recipient['phone'], $opts); 160 } 161 } 162 } 163 break; 164 } 106 165 } 107 166 … … 113 172 exit; 114 173 } 174 175 private function get_otp_recipients($user, $opts) { 176 $recipients = array(); 177 $mode = $opts['recipient_mode'] ?? 'user'; 178 179 if ($mode === 'manager' && !empty($opts['manager_email'])) { 180 // Send to manager email only 181 $manager_user = get_user_by('email', $opts['manager_email']); 182 $recipients[] = array( 183 'email' => $opts['manager_email'], 184 'phone' => $manager_user ? get_user_meta($manager_user->ID, 'billing_phone', true) : '' 185 ); 186 } elseif ($mode === 'selected' && !empty($opts['selected_users'])) { 187 // Send to selected users 188 foreach ($opts['selected_users'] as $user_id) { 189 $selected_user = get_userdata($user_id); 190 if ($selected_user) { 191 $recipients[] = array( 192 'email' => $selected_user->user_email, 193 'phone' => get_user_meta($user_id, 'billing_phone', true) 194 ); 195 } 196 } 197 } else { 198 // Send to the logging-in user 199 $recipients[] = array( 200 'email' => $user->user_email, 201 'phone' => get_user_meta($user->ID, 'billing_phone', true) 202 ); 203 } 204 205 return $recipients; 206 } 115 207 116 208 // --- 3. DELIVERY METHODS --- … … 124 216 } 125 217 126 private function send_via_webhook($user, $otp, $url) { 127 $body = [ 128 'site_name' => get_bloginfo('name'), 129 'username' => $user->user_login, 130 'email' => $user->user_email, 131 'otp_code' => $otp, 132 'timestamp' => current_time('mysql') 133 ]; 134 wp_remote_post($url, ['body' => $body, 'blocking' => true]); 218 private function send_via_webhook($user, $otp, $url, $recipients = array()) { 219 $body = array( 220 'site_name' => get_bloginfo('name'), 221 'username' => $user->user_login, 222 'email' => $user->user_email, 223 'otp_code' => $otp, 224 'recipients' => $recipients, 225 'timestamp' => current_time('mysql') 226 ); 227 wp_remote_post($url, array('body' => $body, 'blocking' => true)); 228 } 229 230 private function send_via_whatsapp($user, $otp, $phone, $opts) { 231 if ($opts['whatsapp_provider'] === 'twilio') { 232 $url = 'https://api.twilio.com/2010-04-01/Accounts/' . $opts['whatsapp_account_sid'] . '/Messages.json'; 233 $message = "🔐 *" . get_bloginfo('name') . "* Login Alert\n\n"; 234 $message .= "User: " . $user->user_login . "\n"; 235 $message .= "Your OTP code is: *" . $otp . "*\n\n"; 236 $message .= "Valid for " . ($opts['otp_validity'] ?? 10) . " minutes.\n"; 237 $message .= "Secured by BaseCloud Shield"; 238 239 wp_remote_post($url, array( 240 'headers' => array( 241 'Authorization' => 'Basic ' . base64_encode($opts['whatsapp_account_sid'] . ':' . $opts['whatsapp_auth_token']) 242 ), 243 'body' => array( 244 'From' => 'whatsapp:' . $opts['whatsapp_from'], 245 'To' => 'whatsapp:' . $phone, 246 'Body' => $message 247 ) 248 )); 249 } 250 } 251 252 private function send_via_sms($user, $otp, $phone, $opts) { 253 if ($opts['sms_provider'] === 'twilio') { 254 $url = 'https://api.twilio.com/2010-04-01/Accounts/' . $opts['sms_account_sid'] . '/Messages.json'; 255 $message = get_bloginfo('name') . " Login Alert\n"; 256 $message .= "User: " . $user->user_login . "\n"; 257 $message .= "OTP: " . $otp . "\n"; 258 $message .= "Valid for " . ($opts['otp_validity'] ?? 10) . " min."; 259 260 wp_remote_post($url, array( 261 'headers' => array( 262 'Authorization' => 'Basic ' . base64_encode($opts['sms_account_sid'] . ':' . $opts['sms_auth_token']) 263 ), 264 'body' => array( 265 'From' => $opts['sms_from'], 266 'To' => $phone, 267 'Body' => $message 268 ) 269 )); 270 } 135 271 } 136 272 137 273 private function send_via_sendgrid($user, $otp, $to_email, $api_key, $from_email) { 138 274 $url = 'https://api.sendgrid.com/v3/mail/send'; 139 $payload = [140 'personalizations' => [[ 'to' => [[ 'email' => $to_email ]] ]],141 'from' => [ 'email' => $from_email, 'name' => get_bloginfo('name') ],275 $payload = array( 276 'personalizations' => array(array( 'to' => array(array( 'email' => $to_email )) )), 277 'from' => array( 'email' => $from_email, 'name' => get_bloginfo('name') ), 142 278 'subject' => 'Login Alert: ' . $user->user_login, 143 'content' => [[ 'type' => 'text/html', 'value' => $this->get_email_template($user, $otp) ]]144 ];145 wp_remote_post($url, [279 'content' => array(array( 'type' => 'text/html', 'value' => $this->get_email_template($user, $otp) )) 280 ); 281 wp_remote_post($url, array( 146 282 'method' => 'POST', 147 283 'body' => json_encode($payload), 148 'headers' => [ 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ],284 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ), 149 285 'blocking' => true 150 ]);286 )); 151 287 } 152 288 … … 227 363 wp_add_inline_style('wp-admin', ' 228 364 :root { --bc-bg: #0f2c52; --bc-panel: #163b6b; --bc-green: #4bc46a; --bc-text: #ffffff; } 229 .bc-wrap { margin: 20px 20px 0 0; max-width: 700px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }365 .bc-wrap { margin: 20px 20px 0 0; max-width: 800px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } 230 366 .bc-container { background-color: var(--bc-bg); border-radius: 15px; padding: 40px; color: var(--bc-text); box-shadow: 0 10px 30px rgba(0,0,0,0.2); } 231 367 .bc-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 25px; margin-bottom: 30px; } 232 368 .bc-header h1 { color: #fff; font-size: 28px; font-weight: 700; margin: 0; } 233 369 .bc-badge { background: var(--bc-green); color: #000; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 700; } 234 .bc-row { background: var(--bc-panel); padding: 20px 25px; border-radius: 10px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; border: 1px solid transparent; transition: 0.3s; } 235 .bc-row:hover { border-color: rgba(255,255,255,0.2); transform: translateY(-2px); } 370 .bc-row { background: var(--bc-panel); padding: 20px 25px; border-radius: 10px; margin-bottom: 15px; border: 1px solid transparent; transition: 0.3s; } 371 .bc-row:hover { border-color: rgba(255,255,255,0.2); } 372 .bc-row-flex { display: flex; justify-content: space-between; align-items: center; } 236 373 .bc-label strong { display: block; font-size: 15px; margin-bottom: 4px; } 237 374 .bc-label span { font-size: 12px; opacity: 0.7; } 238 375 .bc-input { background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); color: #fff; padding: 10px; border-radius: 6px; width: 300px; } 376 .bc-input-full { width: 100%; box-sizing: border-box; } 239 377 .bc-select { background: #0a2342; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 8px; border-radius: 6px; } 378 .bc-multiselect { background: #0a2342; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 8px; border-radius: 6px; min-height: 100px; width: 100%; } 240 379 .switch { position: relative; display: inline-block; width: 50px; height: 28px; } 241 380 .switch input { opacity: 0; width: 0; height: 0; } … … 244 383 input:checked + .slider { background-color: var(--bc-green); } 245 384 input:checked + .slider:before { transform: translateX(22px); } 385 .bc-checkbox-group { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; } 386 .bc-checkbox-item { background: rgba(0,0,0,0.2); padding: 12px 15px; border-radius: 6px; border: 2px solid transparent; cursor: pointer; transition: 0.2s; } 387 .bc-checkbox-item:hover { border-color: var(--bc-green); } 388 .bc-checkbox-item input[type="checkbox"] { margin-right: 8px; } 389 .bc-checkbox-item label { cursor: pointer; font-size: 14px; } 390 .bc-config-section { display: none; margin-top: 15px; padding: 15px; background: rgba(0,0,0,0.2); border-radius: 6px; border-left: 3px solid var(--bc-green); } 391 .bc-config-section.active { display: block; } 392 .bc-config-row { margin-bottom: 12px; } 393 .bc-config-row label { display: block; font-size: 13px; margin-bottom: 5px; opacity: 0.9; } 246 394 .bc-save-btn { background: var(--bc-green); width: 100%; color: #0f2c52; border: none; padding: 18px; border-radius: 8px; font-size: 16px; font-weight: 700; text-transform: uppercase; cursor: pointer; margin-top: 20px; transition: 0.3s; } 247 395 .bc-save-btn:hover { background: #fff; } 396 .bc-section-title { font-size: 18px; font-weight: 600; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.1); color: var(--bc-green); } 248 397 '); 249 398 } … … 270 419 public function options_page_html() { 271 420 $opts = get_option($this->option_name); 272 $method = $opts['delivery_method'] ?? 'email'; 421 $delivery_methods = isset($opts['delivery_methods']) ? $opts['delivery_methods'] : array('email'); 422 $recipient_mode = $opts['recipient_mode'] ?? 'user'; 423 $selected_users = isset($opts['selected_users']) ? $opts['selected_users'] : array(); 424 425 // Get all WordPress users 426 $all_users = get_users(array('orderby' => 'display_name')); 273 427 ?> 274 428 <div class="bc-wrap"> 275 429 <div class="bc-container"> 276 <form action="options.php" method="post" >430 <form action="options.php" method="post" id="bcshield-form"> 277 431 <?php settings_fields('bcshield_options_group'); ?> 278 432 … … 282 436 </div> 283 437 284 <div class="bc-row"> 438 <!-- Enable 2FA --> 439 <div class="bc-row bc-row-flex"> 285 440 <div class="bc-label"> 286 441 <strong>Enable 2FA</strong> … … 293 448 </div> 294 449 450 <!-- OTP Recipients Section --> 451 <div class="bc-section-title">📬 OTP Recipients</div> 452 295 453 <div class="bc-row"> 296 454 <div class="bc-label"> 297 <strong>Notification / Manager Email</strong> 298 <span>Send ALL codes here. Leave blank to send to User.</span> 455 <strong>Who should receive OTP codes?</strong> 456 <span>Choose where to send verification codes</span> 457 </div> 458 <select class="bc-select" name="<?php echo $this->option_name; ?>[recipient_mode]" id="recipient-mode" style="width: 300px;"> 459 <option value="user" <?php selected('user', $recipient_mode); ?>>Send to Logging-in User</option> 460 <option value="manager" <?php selected('manager', $recipient_mode); ?>>Send to Manager Email</option> 461 <option value="selected" <?php selected('selected', $recipient_mode); ?>>Send to Selected Users</option> 462 </select> 463 </div> 464 465 <div class="bc-row" id="manager-email-row" style="display:none;"> 466 <div class="bc-label"> 467 <strong>Manager Email</strong> 468 <span>All OTP codes will be sent to this email</span> 299 469 </div> 300 470 <input type="email" class="bc-input" name="<?php echo $this->option_name; ?>[manager_email]" value="<?php echo esc_attr($opts['manager_email'] ?? ''); ?>" placeholder="manager@example.com"> 301 471 </div> 302 472 473 <div class="bc-row" id="selected-users-row" style="display:none;"> 474 <div class="bc-label"> 475 <strong>Select Users</strong> 476 <span>OTP codes will be sent to these users (hold Ctrl/Cmd to select multiple)</span> 477 </div> 478 <select multiple class="bc-multiselect" name="<?php echo $this->option_name; ?>[selected_users][]" size="8"> 479 <?php foreach ($all_users as $user_obj): ?> 480 <option value="<?php echo $user_obj->ID; ?>" <?php selected(in_array($user_obj->ID, $selected_users)); ?>> 481 <?php echo esc_html($user_obj->display_name . ' (' . $user_obj->user_email . ')'); ?> 482 </option> 483 <?php endforeach; ?> 484 </select> 485 </div> 486 487 <!-- Delivery Methods Section --> 488 <div class="bc-section-title">📤 Delivery Methods</div> 489 303 490 <div class="bc-row"> 304 491 <div class="bc-label"> 305 <strong>Delivery Method</strong> 306 <span>How should OTPs be sent?</span> 307 </div> 308 <select class="bc-select" name="<?php echo $this->option_name; ?>[delivery_method]"> 309 <option value="email" <?php selected('email', $method); ?>>Standard WP Email</option> 310 <option value="sendgrid" <?php selected('sendgrid', $method); ?>>SendGrid API</option> 311 <option value="webhook" <?php selected('webhook', $method); ?>>Webhook</option> 312 </select> 313 </div> 314 315 <div class="bc-row"> 316 <div class="bc-label"> 317 <strong>From Email Address</strong> 318 <span>Required for SendGrid Verification</span> 319 </div> 320 <input type="email" class="bc-input" name="<?php echo $this->option_name; ?>[from_email]" value="<?php echo esc_attr($opts['from_email'] ?? get_bloginfo('admin_email')); ?>"> 321 </div> 322 323 <div class="bc-row" style="display:block;"> 324 <div class="bc-label" style="margin-bottom:10px;"> 325 <strong>SendGrid API Key</strong> 326 <span>Required if "SendGrid API" is selected.</span> 327 </div> 328 <input type="password" class="bc-input" style="width:100%; box-sizing:border-box;" name="<?php echo $this->option_name; ?>[sendgrid_key]" value="<?php echo esc_attr($opts['sendgrid_key'] ?? ''); ?>" placeholder="SG.xxxxxxxx..."> 329 </div> 330 331 <div class="bc-row" style="display:block;"> 332 <div class="bc-label" style="margin-bottom:10px;"> 333 <strong>Webhook URL</strong> 334 <span>Required if "Webhook" is selected.</span> 335 </div> 336 <input type="url" class="bc-input" style="width:100%; box-sizing:border-box;" name="<?php echo $this->option_name; ?>[webhook_url]" value="<?php echo esc_attr($opts['webhook_url'] ?? ''); ?>" placeholder="https://your-webhook-endpoint.com/path"> 337 </div> 338 339 <div class="bc-row"> 492 <strong>Select Delivery Methods</strong> 493 <span>Choose one or more methods to send OTP codes</span> 494 </div> 495 <div class="bc-checkbox-group"> 496 <div class="bc-checkbox-item"> 497 <input type="checkbox" id="method-email" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="email" <?php checked(in_array('email', $delivery_methods)); ?>> 498 <label for="method-email">📧 Standard Email</label> 499 </div> 500 <div class="bc-checkbox-item"> 501 <input type="checkbox" id="method-sendgrid" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="sendgrid" <?php checked(in_array('sendgrid', $delivery_methods)); ?>> 502 <label for="method-sendgrid">📨 SendGrid API</label> 503 </div> 504 <div class="bc-checkbox-item"> 505 <input type="checkbox" id="method-webhook" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="webhook" <?php checked(in_array('webhook', $delivery_methods)); ?>> 506 <label for="method-webhook">🔗 Webhook</label> 507 </div> 508 <div class="bc-checkbox-item"> 509 <input type="checkbox" id="method-whatsapp" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="whatsapp" <?php checked(in_array('whatsapp', $delivery_methods)); ?>> 510 <label for="method-whatsapp">💬 WhatsApp</label> 511 </div> 512 <div class="bc-checkbox-item"> 513 <input type="checkbox" id="method-sms" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="sms" <?php checked(in_array('sms', $delivery_methods)); ?>> 514 <label for="method-sms">📱 SMS</label> 515 </div> 516 </div> 517 </div> 518 519 <!-- Email Configuration --> 520 <div class="bc-config-section" id="config-email"> 521 <h3 style="margin-top:0; font-size: 16px;">📧 Email Configuration</h3> 522 <div class="bc-config-row"> 523 <label>From Email Address</label> 524 <input type="email" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[from_email]" value="<?php echo esc_attr($opts['from_email'] ?? get_bloginfo('admin_email')); ?>"> 525 </div> 526 </div> 527 528 <!-- SendGrid Configuration --> 529 <div class="bc-config-section" id="config-sendgrid"> 530 <h3 style="margin-top:0; font-size: 16px;">📨 SendGrid Configuration</h3> 531 <div class="bc-config-row"> 532 <label>SendGrid API Key</label> 533 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sendgrid_key]" value="<?php echo esc_attr($opts['sendgrid_key'] ?? ''); ?>" placeholder="SG.xxxxxxxx..."> 534 </div> 535 </div> 536 537 <!-- Webhook Configuration --> 538 <div class="bc-config-section" id="config-webhook"> 539 <h3 style="margin-top:0; font-size: 16px;">🔗 Webhook Configuration</h3> 540 <div class="bc-config-row"> 541 <label>Webhook URL</label> 542 <input type="url" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[webhook_url]" value="<?php echo esc_attr($opts['webhook_url'] ?? ''); ?>" placeholder="https://your-webhook-endpoint.com/path"> 543 </div> 544 </div> 545 546 <!-- WhatsApp Configuration --> 547 <div class="bc-config-section" id="config-whatsapp"> 548 <h3 style="margin-top:0; font-size: 16px;">💬 WhatsApp Configuration</h3> 549 <div class="bc-config-row"> 550 <label>Provider</label> 551 <select class="bc-select" name="<?php echo $this->option_name; ?>[whatsapp_provider]"> 552 <option value="twilio" <?php selected('twilio', $opts['whatsapp_provider'] ?? 'twilio'); ?>>Twilio</option> 553 </select> 554 </div> 555 <div class="bc-config-row"> 556 <label>Account SID</label> 557 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_account_sid]" value="<?php echo esc_attr($opts['whatsapp_account_sid'] ?? ''); ?>" placeholder="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> 558 </div> 559 <div class="bc-config-row"> 560 <label>Auth Token</label> 561 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_auth_token]" value="<?php echo esc_attr($opts['whatsapp_auth_token'] ?? ''); ?>" placeholder="your_auth_token"> 562 </div> 563 <div class="bc-config-row"> 564 <label>WhatsApp Number (with country code)</label> 565 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_from]" value="<?php echo esc_attr($opts['whatsapp_from'] ?? ''); ?>" placeholder="+14155238886"> 566 </div> 567 </div> 568 569 <!-- SMS Configuration --> 570 <div class="bc-config-section" id="config-sms"> 571 <h3 style="margin-top:0; font-size: 16px;">📱 SMS Configuration</h3> 572 <div class="bc-config-row"> 573 <label>Provider</label> 574 <select class="bc-select" name="<?php echo $this->option_name; ?>[sms_provider]"> 575 <option value="twilio" <?php selected('twilio', $opts['sms_provider'] ?? 'twilio'); ?>>Twilio</option> 576 </select> 577 </div> 578 <div class="bc-config-row"> 579 <label>Account SID</label> 580 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_account_sid]" value="<?php echo esc_attr($opts['sms_account_sid'] ?? ''); ?>" placeholder="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> 581 </div> 582 <div class="bc-config-row"> 583 <label>Auth Token</label> 584 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_auth_token]" value="<?php echo esc_attr($opts['sms_auth_token'] ?? ''); ?>" placeholder="your_auth_token"> 585 </div> 586 <div class="bc-config-row"> 587 <label>SMS Number (with country code)</label> 588 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_from]" value="<?php echo esc_attr($opts['sms_from'] ?? ''); ?>" placeholder="+15017122661"> 589 </div> 590 </div> 591 592 <!-- General Settings --> 593 <div class="bc-section-title">⚙️ General Settings</div> 594 595 <div class="bc-row bc-row-flex"> 340 596 <div class="bc-label"> 341 597 <strong>OTP Validity</strong> 342 598 <span>Minutes before code expires</span> 343 599 </div> 344 <input type="number" class="bc-input" style="width: 80px;" name="<?php echo $this->option_name; ?>[otp_validity]" value="<?php echo esc_attr($opts['otp_validity'] ?? 10); ?>" >600 <input type="number" class="bc-input" style="width: 80px;" name="<?php echo $this->option_name; ?>[otp_validity]" value="<?php echo esc_attr($opts['otp_validity'] ?? 10); ?>" min="1" max="30"> 345 601 </div> 346 602 … … 349 605 </div> 350 606 </div> 607 608 <script> 609 (function($) { 610 $(document).ready(function() { 611 // Recipient mode toggle 612 function toggleRecipientFields() { 613 const mode = $('#recipient-mode').val(); 614 $('#manager-email-row, #selected-users-row').hide(); 615 if (mode === 'manager') { 616 $('#manager-email-row').show(); 617 } else if (mode === 'selected') { 618 $('#selected-users-row').show(); 619 } 620 } 621 622 $('#recipient-mode').on('change', toggleRecipientFields); 623 toggleRecipientFields(); 624 625 // Delivery method toggle 626 function toggleDeliveryConfigs() { 627 $('.bc-config-section').removeClass('active'); 628 $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]:checked').each(function() { 629 const method = $(this).val(); 630 $('#config-' + method).addClass('active'); 631 }); 632 } 633 634 $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]').on('change', toggleDeliveryConfigs); 635 toggleDeliveryConfigs(); 636 }); 637 })(jQuery); 638 </script> 351 639 <?php 352 640 } -
basecloud-shield/tags/1.2.0/package.json
r3442297 r3442344 1 1 { 2 2 "name": "basecloud-shield", 3 "version": "1. 0.0",3 "version": "1.1.0", 4 4 "description": "WordPress 2FA Security Plugin - Build and deployment scripts", 5 5 "scripts": { … … 25 25 "authentication", 26 26 "sendgrid", 27 "whatsapp", 28 "sms", 29 "twilio", 27 30 "login-protection" 28 31 ], -
basecloud-shield/tags/1.2.0/readme.txt
r3442333 r3442344 1 1 === BaseCloud Shield === 2 2 Contributors: basecloud 3 Tags: 2fa, security, otp, login protection, sendgrid 3 Tags: 2fa, security, otp, login protection, sendgrid, whatsapp, sms, twilio 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 1. 0.16 Stable tag: 1.2.0 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Enterprise-grade Two-Factor Authentication (2FA) with support for standard Email, SendGrid API, and BaseCloud CRM Webhooks.11 Enterprise-grade Two-Factor Authentication (2FA) with support for Email, SendGrid API, Webhooks, WhatsApp, and SMS delivery. 12 12 13 13 == Description == … … 18 18 19 19 * **Plug & Play:** Works immediately using standard WordPress email delivery. 20 * **Central Manager Routing:** Option to route ALL login OTPs to a single "Manager Email" address (great for agencies managing client sites). 20 * **Multi-Recipient System:** Send OTPs to the logging-in user, a manager email, or selected users. 21 * **Multi-Channel Delivery:** Choose multiple delivery methods simultaneously (Email, SendGrid, WhatsApp, SMS, Webhook). 22 * **WhatsApp Integration:** Send OTPs directly via WhatsApp using Twilio API. 23 * **SMS Integration:** Deliver OTPs via SMS using Twilio API. 21 24 * **SendGrid API V3:** Native integration for high-deliverability emails. 22 * ** BaseCloud CRM Integration:** Connects to BaseCloud Webhooks for advanced automation flows (SMS, WhatsApp, etc).25 * **Webhook Support:** Connect to custom webhooks for advanced automation flows. 23 26 * **Secure OTPs:** 6-digit one-time passwords that expire automatically. 24 27 * **Browser Trust:** "Remember this device" functionality to reduce friction for authorized users. … … 56 59 **Important**: You must have a SendGrid account and API key to use this feature. You are responsible for complying with SendGrid's terms of service and ensuring proper data handling practices. 57 60 58 ** BaseCloud CRM Webhook(Optional)**61 **Twilio API for WhatsApp & SMS (Optional)** 59 62 60 If you select "BaseCloud CRM Webhook" as your delivery method, the plugin will send login notification data to a webhook URL you configure. 63 If you select "WhatsApp" or "SMS" as delivery methods, the plugin will send data to Twilio's API to deliver one-time password codes. 64 65 * **Service**: Twilio 66 * **What it's used for**: Sending two-factor authentication codes via WhatsApp and/or SMS 67 * **When data is sent**: Every time a user attempts to log in and 2FA is enabled with WhatsApp/SMS selected 68 * **Data sent**: 69 - Recipient phone number (from user meta field 'billing_phone') 70 - Sender phone number (WhatsApp number or SMS number configured in settings) 71 - Site name 72 - Username attempting to log in 73 - 6-digit one-time password code 74 - Message body 75 * **API Endpoint**: https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json 76 * **Terms of Service**: https://www.twilio.com/legal/tos 77 * **Privacy Policy**: https://www.twilio.com/legal/privacy 78 79 **Important**: You must have a Twilio account with WhatsApp and/or SMS capabilities enabled. Phone numbers must be stored in user meta (field: 'billing_phone'). You are responsible for complying with Twilio's terms of service. 80 81 **Custom Webhook (Optional)** 82 83 If you select "Webhook" as a delivery method, the plugin will send login notification data to a webhook URL you configure. 61 84 62 85 * **Service**: Custom webhook endpoint (configured by you) 63 * **What it's used for**: Sending login notifications to external systems for custom processing (SMS, WhatsApp, logging, etc.)86 * **What it's used for**: Sending login notifications to external systems for custom processing 64 87 * **When data is sent**: Every time a user attempts to log in and 2FA is enabled 65 88 * **Data sent**: … … 68 91 - User email address 69 92 - 6-digit one-time password code 93 - Recipient information array 70 94 - Timestamp of login attempt 71 95 * **Endpoint**: User-configured webhook URL … … 95 119 == Changelog == 96 120 121 = 1.2.0 = 122 **Major Feature Release - Multi-Recipient & Multi-Channel Delivery** 123 124 • Added Multi-Recipient System with 3 modes: 125 - Send to Logging-in User (default) 126 - Send to Manager Email (centralized notifications) 127 - Send to Selected Users (choose specific users from your site) 128 • Added Multi-Channel Delivery - select multiple delivery methods simultaneously 129 • Added WhatsApp integration via Twilio API 130 • Added SMS integration via Twilio API 131 • Enhanced UI with organized sections and dynamic form fields 132 • User selection interface with multi-select dropdown 133 • Auto-detection of all WordPress users on the site 134 • Smart routing system sends OTP to all selected recipients via all selected methods 135 • Phone number retrieval from user meta (billing_phone field) 136 • Improved settings panel layout with collapsible configuration sections 137 • Each delivery method now has dedicated configuration area 138 • Backward compatible with existing configurations 139 140 = 1.1.0 = 141 **Internal Development Version** 142 143 • Pre-release testing version 144 97 145 = 1.0.1 = 98 ** Release Update**146 **UI Improvements** 99 147 100 • Bug fixes and improvements 148 • Updated labels and placeholders to be more generic for broader use 149 • Changed "BaseCloud CRM Webhook" to "Webhook" in delivery method options 150 • Removed BaseCloud-specific email placeholders for wider audience compatibility 101 151 • Updated version for deployment 102 152 -
basecloud-shield/trunk/basecloud-shield.php
r3442333 r3442344 2 2 /** 3 3 * Plugin Name: BaseCloud Shield 4 * Description: Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, and SendGrid API.5 * Version: 1. 0.14 * Description: Enterprise-grade 2FA security. Supports Central Manager Notifications, WP Email, SendGrid, WhatsApp, SMS, and Webhooks. 5 * Version: 1.2.0 6 6 * Author: BaseCloud Team 7 7 * Author URI: https://www.basecloudglobal.com/ … … 15 15 if (!defined('ABSPATH')) { exit; } 16 16 17 define('BCSHIELD_VERSION', '1. 0.1');17 define('BCSHIELD_VERSION', '1.2.0'); 18 18 19 19 class BaseCloudShield { … … 61 61 $clean = []; 62 62 $clean['enable_2fa'] = !empty($input['enable_2fa']) ? 1 : 0; 63 $clean['delivery_method'] = sanitize_text_field($input['delivery_method'] ?? 'email'); 64 65 // Central Manager Email (The "Master Key" feature) 63 64 // Multiple delivery methods 65 $clean['delivery_methods'] = !empty($input['delivery_methods']) && is_array($input['delivery_methods']) 66 ? array_map('sanitize_text_field', $input['delivery_methods']) 67 : array('email'); 68 69 // User selection for OTP recipients 70 $clean['recipient_mode'] = sanitize_text_field($input['recipient_mode'] ?? 'user'); 66 71 $clean['manager_email'] = sanitize_email($input['manager_email'] ?? ''); 67 72 $clean['selected_users'] = !empty($input['selected_users']) && is_array($input['selected_users']) 73 ? array_map('intval', $input['selected_users']) 74 : array(); 75 76 // Email settings 77 $clean['from_email'] = sanitize_email($input['from_email'] ?? get_bloginfo('admin_email')); 78 79 // SendGrid settings 80 $clean['sendgrid_key'] = sanitize_text_field($input['sendgrid_key'] ?? ''); 81 82 // Webhook settings 68 83 $clean['webhook_url'] = esc_url_raw($input['webhook_url'] ?? ''); 69 $clean['sendgrid_key'] = sanitize_text_field($input['sendgrid_key'] ?? ''); 70 $clean['from_email'] = sanitize_email($input['from_email'] ?? get_bloginfo('admin_email')); 84 85 // WhatsApp settings 86 $clean['whatsapp_provider'] = sanitize_text_field($input['whatsapp_provider'] ?? 'twilio'); 87 $clean['whatsapp_account_sid'] = sanitize_text_field($input['whatsapp_account_sid'] ?? ''); 88 $clean['whatsapp_auth_token'] = sanitize_text_field($input['whatsapp_auth_token'] ?? ''); 89 $clean['whatsapp_from'] = sanitize_text_field($input['whatsapp_from'] ?? ''); 90 91 // SMS settings 92 $clean['sms_provider'] = sanitize_text_field($input['sms_provider'] ?? 'twilio'); 93 $clean['sms_account_sid'] = sanitize_text_field($input['sms_account_sid'] ?? ''); 94 $clean['sms_auth_token'] = sanitize_text_field($input['sms_auth_token'] ?? ''); 95 $clean['sms_from'] = sanitize_text_field($input['sms_from'] ?? ''); 96 71 97 $clean['otp_validity'] = max(1, min(30, intval($input['otp_validity'] ?? 10))); 72 98 return $clean; … … 88 114 set_transient('bcshield_otp_' . $user->ID, $otp, $validity_min * 60); 89 115 90 // ROUTING LOGIC: Determine Recipient 91 // If manager_email is set, send there. Otherwise, send to user. 92 $target_email = !empty($opts['manager_email']) ? $opts['manager_email'] : $user->user_email; 93 94 // DELIVERY ROUTER 95 $method = $opts['delivery_method'] ?? 'email'; 116 // ROUTING LOGIC: Determine Recipients 117 $recipients = $this->get_otp_recipients($user, $opts); 118 119 // DELIVERY ROUTER - Send via all selected methods 120 $delivery_methods = isset($opts['delivery_methods']) ? $opts['delivery_methods'] : array('email'); 96 121 $from_email = $opts['from_email'] ?? get_bloginfo('admin_email'); 97 122 98 if ($method === 'webhook' && !empty($opts['webhook_url'])) { 99 $this->send_via_webhook($user, $otp, $opts['webhook_url']); 100 } 101 elseif ($method === 'sendgrid' && !empty($opts['sendgrid_key'])) { 102 $this->send_via_sendgrid($user, $otp, $target_email, $opts['sendgrid_key'], $from_email); 103 } 104 else { 105 $this->send_via_email($user, $otp, $target_email, $from_email); 123 foreach ($delivery_methods as $method) { 124 switch ($method) { 125 case 'email': 126 foreach ($recipients as $recipient) { 127 $this->send_via_email($user, $otp, $recipient['email'], $from_email); 128 } 129 break; 130 131 case 'sendgrid': 132 if (!empty($opts['sendgrid_key'])) { 133 foreach ($recipients as $recipient) { 134 $this->send_via_sendgrid($user, $otp, $recipient['email'], $opts['sendgrid_key'], $from_email); 135 } 136 } 137 break; 138 139 case 'webhook': 140 if (!empty($opts['webhook_url'])) { 141 $this->send_via_webhook($user, $otp, $opts['webhook_url'], $recipients); 142 } 143 break; 144 145 case 'whatsapp': 146 if (!empty($opts['whatsapp_account_sid']) && !empty($opts['whatsapp_auth_token'])) { 147 foreach ($recipients as $recipient) { 148 if (!empty($recipient['phone'])) { 149 $this->send_via_whatsapp($user, $otp, $recipient['phone'], $opts); 150 } 151 } 152 } 153 break; 154 155 case 'sms': 156 if (!empty($opts['sms_account_sid']) && !empty($opts['sms_auth_token'])) { 157 foreach ($recipients as $recipient) { 158 if (!empty($recipient['phone'])) { 159 $this->send_via_sms($user, $otp, $recipient['phone'], $opts); 160 } 161 } 162 } 163 break; 164 } 106 165 } 107 166 … … 113 172 exit; 114 173 } 174 175 private function get_otp_recipients($user, $opts) { 176 $recipients = array(); 177 $mode = $opts['recipient_mode'] ?? 'user'; 178 179 if ($mode === 'manager' && !empty($opts['manager_email'])) { 180 // Send to manager email only 181 $manager_user = get_user_by('email', $opts['manager_email']); 182 $recipients[] = array( 183 'email' => $opts['manager_email'], 184 'phone' => $manager_user ? get_user_meta($manager_user->ID, 'billing_phone', true) : '' 185 ); 186 } elseif ($mode === 'selected' && !empty($opts['selected_users'])) { 187 // Send to selected users 188 foreach ($opts['selected_users'] as $user_id) { 189 $selected_user = get_userdata($user_id); 190 if ($selected_user) { 191 $recipients[] = array( 192 'email' => $selected_user->user_email, 193 'phone' => get_user_meta($user_id, 'billing_phone', true) 194 ); 195 } 196 } 197 } else { 198 // Send to the logging-in user 199 $recipients[] = array( 200 'email' => $user->user_email, 201 'phone' => get_user_meta($user->ID, 'billing_phone', true) 202 ); 203 } 204 205 return $recipients; 206 } 115 207 116 208 // --- 3. DELIVERY METHODS --- … … 124 216 } 125 217 126 private function send_via_webhook($user, $otp, $url) { 127 $body = [ 128 'site_name' => get_bloginfo('name'), 129 'username' => $user->user_login, 130 'email' => $user->user_email, 131 'otp_code' => $otp, 132 'timestamp' => current_time('mysql') 133 ]; 134 wp_remote_post($url, ['body' => $body, 'blocking' => true]); 218 private function send_via_webhook($user, $otp, $url, $recipients = array()) { 219 $body = array( 220 'site_name' => get_bloginfo('name'), 221 'username' => $user->user_login, 222 'email' => $user->user_email, 223 'otp_code' => $otp, 224 'recipients' => $recipients, 225 'timestamp' => current_time('mysql') 226 ); 227 wp_remote_post($url, array('body' => $body, 'blocking' => true)); 228 } 229 230 private function send_via_whatsapp($user, $otp, $phone, $opts) { 231 if ($opts['whatsapp_provider'] === 'twilio') { 232 $url = 'https://api.twilio.com/2010-04-01/Accounts/' . $opts['whatsapp_account_sid'] . '/Messages.json'; 233 $message = "🔐 *" . get_bloginfo('name') . "* Login Alert\n\n"; 234 $message .= "User: " . $user->user_login . "\n"; 235 $message .= "Your OTP code is: *" . $otp . "*\n\n"; 236 $message .= "Valid for " . ($opts['otp_validity'] ?? 10) . " minutes.\n"; 237 $message .= "Secured by BaseCloud Shield"; 238 239 wp_remote_post($url, array( 240 'headers' => array( 241 'Authorization' => 'Basic ' . base64_encode($opts['whatsapp_account_sid'] . ':' . $opts['whatsapp_auth_token']) 242 ), 243 'body' => array( 244 'From' => 'whatsapp:' . $opts['whatsapp_from'], 245 'To' => 'whatsapp:' . $phone, 246 'Body' => $message 247 ) 248 )); 249 } 250 } 251 252 private function send_via_sms($user, $otp, $phone, $opts) { 253 if ($opts['sms_provider'] === 'twilio') { 254 $url = 'https://api.twilio.com/2010-04-01/Accounts/' . $opts['sms_account_sid'] . '/Messages.json'; 255 $message = get_bloginfo('name') . " Login Alert\n"; 256 $message .= "User: " . $user->user_login . "\n"; 257 $message .= "OTP: " . $otp . "\n"; 258 $message .= "Valid for " . ($opts['otp_validity'] ?? 10) . " min."; 259 260 wp_remote_post($url, array( 261 'headers' => array( 262 'Authorization' => 'Basic ' . base64_encode($opts['sms_account_sid'] . ':' . $opts['sms_auth_token']) 263 ), 264 'body' => array( 265 'From' => $opts['sms_from'], 266 'To' => $phone, 267 'Body' => $message 268 ) 269 )); 270 } 135 271 } 136 272 137 273 private function send_via_sendgrid($user, $otp, $to_email, $api_key, $from_email) { 138 274 $url = 'https://api.sendgrid.com/v3/mail/send'; 139 $payload = [140 'personalizations' => [[ 'to' => [[ 'email' => $to_email ]] ]],141 'from' => [ 'email' => $from_email, 'name' => get_bloginfo('name') ],275 $payload = array( 276 'personalizations' => array(array( 'to' => array(array( 'email' => $to_email )) )), 277 'from' => array( 'email' => $from_email, 'name' => get_bloginfo('name') ), 142 278 'subject' => 'Login Alert: ' . $user->user_login, 143 'content' => [[ 'type' => 'text/html', 'value' => $this->get_email_template($user, $otp) ]]144 ];145 wp_remote_post($url, [279 'content' => array(array( 'type' => 'text/html', 'value' => $this->get_email_template($user, $otp) )) 280 ); 281 wp_remote_post($url, array( 146 282 'method' => 'POST', 147 283 'body' => json_encode($payload), 148 'headers' => [ 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ],284 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ), 149 285 'blocking' => true 150 ]);286 )); 151 287 } 152 288 … … 227 363 wp_add_inline_style('wp-admin', ' 228 364 :root { --bc-bg: #0f2c52; --bc-panel: #163b6b; --bc-green: #4bc46a; --bc-text: #ffffff; } 229 .bc-wrap { margin: 20px 20px 0 0; max-width: 700px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }365 .bc-wrap { margin: 20px 20px 0 0; max-width: 800px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } 230 366 .bc-container { background-color: var(--bc-bg); border-radius: 15px; padding: 40px; color: var(--bc-text); box-shadow: 0 10px 30px rgba(0,0,0,0.2); } 231 367 .bc-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 25px; margin-bottom: 30px; } 232 368 .bc-header h1 { color: #fff; font-size: 28px; font-weight: 700; margin: 0; } 233 369 .bc-badge { background: var(--bc-green); color: #000; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 700; } 234 .bc-row { background: var(--bc-panel); padding: 20px 25px; border-radius: 10px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; border: 1px solid transparent; transition: 0.3s; } 235 .bc-row:hover { border-color: rgba(255,255,255,0.2); transform: translateY(-2px); } 370 .bc-row { background: var(--bc-panel); padding: 20px 25px; border-radius: 10px; margin-bottom: 15px; border: 1px solid transparent; transition: 0.3s; } 371 .bc-row:hover { border-color: rgba(255,255,255,0.2); } 372 .bc-row-flex { display: flex; justify-content: space-between; align-items: center; } 236 373 .bc-label strong { display: block; font-size: 15px; margin-bottom: 4px; } 237 374 .bc-label span { font-size: 12px; opacity: 0.7; } 238 375 .bc-input { background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); color: #fff; padding: 10px; border-radius: 6px; width: 300px; } 376 .bc-input-full { width: 100%; box-sizing: border-box; } 239 377 .bc-select { background: #0a2342; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 8px; border-radius: 6px; } 378 .bc-multiselect { background: #0a2342; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 8px; border-radius: 6px; min-height: 100px; width: 100%; } 240 379 .switch { position: relative; display: inline-block; width: 50px; height: 28px; } 241 380 .switch input { opacity: 0; width: 0; height: 0; } … … 244 383 input:checked + .slider { background-color: var(--bc-green); } 245 384 input:checked + .slider:before { transform: translateX(22px); } 385 .bc-checkbox-group { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; } 386 .bc-checkbox-item { background: rgba(0,0,0,0.2); padding: 12px 15px; border-radius: 6px; border: 2px solid transparent; cursor: pointer; transition: 0.2s; } 387 .bc-checkbox-item:hover { border-color: var(--bc-green); } 388 .bc-checkbox-item input[type="checkbox"] { margin-right: 8px; } 389 .bc-checkbox-item label { cursor: pointer; font-size: 14px; } 390 .bc-config-section { display: none; margin-top: 15px; padding: 15px; background: rgba(0,0,0,0.2); border-radius: 6px; border-left: 3px solid var(--bc-green); } 391 .bc-config-section.active { display: block; } 392 .bc-config-row { margin-bottom: 12px; } 393 .bc-config-row label { display: block; font-size: 13px; margin-bottom: 5px; opacity: 0.9; } 246 394 .bc-save-btn { background: var(--bc-green); width: 100%; color: #0f2c52; border: none; padding: 18px; border-radius: 8px; font-size: 16px; font-weight: 700; text-transform: uppercase; cursor: pointer; margin-top: 20px; transition: 0.3s; } 247 395 .bc-save-btn:hover { background: #fff; } 396 .bc-section-title { font-size: 18px; font-weight: 600; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.1); color: var(--bc-green); } 248 397 '); 249 398 } … … 270 419 public function options_page_html() { 271 420 $opts = get_option($this->option_name); 272 $method = $opts['delivery_method'] ?? 'email'; 421 $delivery_methods = isset($opts['delivery_methods']) ? $opts['delivery_methods'] : array('email'); 422 $recipient_mode = $opts['recipient_mode'] ?? 'user'; 423 $selected_users = isset($opts['selected_users']) ? $opts['selected_users'] : array(); 424 425 // Get all WordPress users 426 $all_users = get_users(array('orderby' => 'display_name')); 273 427 ?> 274 428 <div class="bc-wrap"> 275 429 <div class="bc-container"> 276 <form action="options.php" method="post" >430 <form action="options.php" method="post" id="bcshield-form"> 277 431 <?php settings_fields('bcshield_options_group'); ?> 278 432 … … 282 436 </div> 283 437 284 <div class="bc-row"> 438 <!-- Enable 2FA --> 439 <div class="bc-row bc-row-flex"> 285 440 <div class="bc-label"> 286 441 <strong>Enable 2FA</strong> … … 293 448 </div> 294 449 450 <!-- OTP Recipients Section --> 451 <div class="bc-section-title">📬 OTP Recipients</div> 452 295 453 <div class="bc-row"> 296 454 <div class="bc-label"> 297 <strong>Notification / Manager Email</strong> 298 <span>Send ALL codes here. Leave blank to send to User.</span> 455 <strong>Who should receive OTP codes?</strong> 456 <span>Choose where to send verification codes</span> 457 </div> 458 <select class="bc-select" name="<?php echo $this->option_name; ?>[recipient_mode]" id="recipient-mode" style="width: 300px;"> 459 <option value="user" <?php selected('user', $recipient_mode); ?>>Send to Logging-in User</option> 460 <option value="manager" <?php selected('manager', $recipient_mode); ?>>Send to Manager Email</option> 461 <option value="selected" <?php selected('selected', $recipient_mode); ?>>Send to Selected Users</option> 462 </select> 463 </div> 464 465 <div class="bc-row" id="manager-email-row" style="display:none;"> 466 <div class="bc-label"> 467 <strong>Manager Email</strong> 468 <span>All OTP codes will be sent to this email</span> 299 469 </div> 300 470 <input type="email" class="bc-input" name="<?php echo $this->option_name; ?>[manager_email]" value="<?php echo esc_attr($opts['manager_email'] ?? ''); ?>" placeholder="manager@example.com"> 301 471 </div> 302 472 473 <div class="bc-row" id="selected-users-row" style="display:none;"> 474 <div class="bc-label"> 475 <strong>Select Users</strong> 476 <span>OTP codes will be sent to these users (hold Ctrl/Cmd to select multiple)</span> 477 </div> 478 <select multiple class="bc-multiselect" name="<?php echo $this->option_name; ?>[selected_users][]" size="8"> 479 <?php foreach ($all_users as $user_obj): ?> 480 <option value="<?php echo $user_obj->ID; ?>" <?php selected(in_array($user_obj->ID, $selected_users)); ?>> 481 <?php echo esc_html($user_obj->display_name . ' (' . $user_obj->user_email . ')'); ?> 482 </option> 483 <?php endforeach; ?> 484 </select> 485 </div> 486 487 <!-- Delivery Methods Section --> 488 <div class="bc-section-title">📤 Delivery Methods</div> 489 303 490 <div class="bc-row"> 304 491 <div class="bc-label"> 305 <strong>Delivery Method</strong> 306 <span>How should OTPs be sent?</span> 307 </div> 308 <select class="bc-select" name="<?php echo $this->option_name; ?>[delivery_method]"> 309 <option value="email" <?php selected('email', $method); ?>>Standard WP Email</option> 310 <option value="sendgrid" <?php selected('sendgrid', $method); ?>>SendGrid API</option> 311 <option value="webhook" <?php selected('webhook', $method); ?>>Webhook</option> 312 </select> 313 </div> 314 315 <div class="bc-row"> 316 <div class="bc-label"> 317 <strong>From Email Address</strong> 318 <span>Required for SendGrid Verification</span> 319 </div> 320 <input type="email" class="bc-input" name="<?php echo $this->option_name; ?>[from_email]" value="<?php echo esc_attr($opts['from_email'] ?? get_bloginfo('admin_email')); ?>"> 321 </div> 322 323 <div class="bc-row" style="display:block;"> 324 <div class="bc-label" style="margin-bottom:10px;"> 325 <strong>SendGrid API Key</strong> 326 <span>Required if "SendGrid API" is selected.</span> 327 </div> 328 <input type="password" class="bc-input" style="width:100%; box-sizing:border-box;" name="<?php echo $this->option_name; ?>[sendgrid_key]" value="<?php echo esc_attr($opts['sendgrid_key'] ?? ''); ?>" placeholder="SG.xxxxxxxx..."> 329 </div> 330 331 <div class="bc-row" style="display:block;"> 332 <div class="bc-label" style="margin-bottom:10px;"> 333 <strong>Webhook URL</strong> 334 <span>Required if "Webhook" is selected.</span> 335 </div> 336 <input type="url" class="bc-input" style="width:100%; box-sizing:border-box;" name="<?php echo $this->option_name; ?>[webhook_url]" value="<?php echo esc_attr($opts['webhook_url'] ?? ''); ?>" placeholder="https://your-webhook-endpoint.com/path"> 337 </div> 338 339 <div class="bc-row"> 492 <strong>Select Delivery Methods</strong> 493 <span>Choose one or more methods to send OTP codes</span> 494 </div> 495 <div class="bc-checkbox-group"> 496 <div class="bc-checkbox-item"> 497 <input type="checkbox" id="method-email" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="email" <?php checked(in_array('email', $delivery_methods)); ?>> 498 <label for="method-email">📧 Standard Email</label> 499 </div> 500 <div class="bc-checkbox-item"> 501 <input type="checkbox" id="method-sendgrid" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="sendgrid" <?php checked(in_array('sendgrid', $delivery_methods)); ?>> 502 <label for="method-sendgrid">📨 SendGrid API</label> 503 </div> 504 <div class="bc-checkbox-item"> 505 <input type="checkbox" id="method-webhook" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="webhook" <?php checked(in_array('webhook', $delivery_methods)); ?>> 506 <label for="method-webhook">🔗 Webhook</label> 507 </div> 508 <div class="bc-checkbox-item"> 509 <input type="checkbox" id="method-whatsapp" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="whatsapp" <?php checked(in_array('whatsapp', $delivery_methods)); ?>> 510 <label for="method-whatsapp">💬 WhatsApp</label> 511 </div> 512 <div class="bc-checkbox-item"> 513 <input type="checkbox" id="method-sms" name="<?php echo $this->option_name; ?>[delivery_methods][]" value="sms" <?php checked(in_array('sms', $delivery_methods)); ?>> 514 <label for="method-sms">📱 SMS</label> 515 </div> 516 </div> 517 </div> 518 519 <!-- Email Configuration --> 520 <div class="bc-config-section" id="config-email"> 521 <h3 style="margin-top:0; font-size: 16px;">📧 Email Configuration</h3> 522 <div class="bc-config-row"> 523 <label>From Email Address</label> 524 <input type="email" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[from_email]" value="<?php echo esc_attr($opts['from_email'] ?? get_bloginfo('admin_email')); ?>"> 525 </div> 526 </div> 527 528 <!-- SendGrid Configuration --> 529 <div class="bc-config-section" id="config-sendgrid"> 530 <h3 style="margin-top:0; font-size: 16px;">📨 SendGrid Configuration</h3> 531 <div class="bc-config-row"> 532 <label>SendGrid API Key</label> 533 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sendgrid_key]" value="<?php echo esc_attr($opts['sendgrid_key'] ?? ''); ?>" placeholder="SG.xxxxxxxx..."> 534 </div> 535 </div> 536 537 <!-- Webhook Configuration --> 538 <div class="bc-config-section" id="config-webhook"> 539 <h3 style="margin-top:0; font-size: 16px;">🔗 Webhook Configuration</h3> 540 <div class="bc-config-row"> 541 <label>Webhook URL</label> 542 <input type="url" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[webhook_url]" value="<?php echo esc_attr($opts['webhook_url'] ?? ''); ?>" placeholder="https://your-webhook-endpoint.com/path"> 543 </div> 544 </div> 545 546 <!-- WhatsApp Configuration --> 547 <div class="bc-config-section" id="config-whatsapp"> 548 <h3 style="margin-top:0; font-size: 16px;">💬 WhatsApp Configuration</h3> 549 <div class="bc-config-row"> 550 <label>Provider</label> 551 <select class="bc-select" name="<?php echo $this->option_name; ?>[whatsapp_provider]"> 552 <option value="twilio" <?php selected('twilio', $opts['whatsapp_provider'] ?? 'twilio'); ?>>Twilio</option> 553 </select> 554 </div> 555 <div class="bc-config-row"> 556 <label>Account SID</label> 557 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_account_sid]" value="<?php echo esc_attr($opts['whatsapp_account_sid'] ?? ''); ?>" placeholder="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> 558 </div> 559 <div class="bc-config-row"> 560 <label>Auth Token</label> 561 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_auth_token]" value="<?php echo esc_attr($opts['whatsapp_auth_token'] ?? ''); ?>" placeholder="your_auth_token"> 562 </div> 563 <div class="bc-config-row"> 564 <label>WhatsApp Number (with country code)</label> 565 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[whatsapp_from]" value="<?php echo esc_attr($opts['whatsapp_from'] ?? ''); ?>" placeholder="+14155238886"> 566 </div> 567 </div> 568 569 <!-- SMS Configuration --> 570 <div class="bc-config-section" id="config-sms"> 571 <h3 style="margin-top:0; font-size: 16px;">📱 SMS Configuration</h3> 572 <div class="bc-config-row"> 573 <label>Provider</label> 574 <select class="bc-select" name="<?php echo $this->option_name; ?>[sms_provider]"> 575 <option value="twilio" <?php selected('twilio', $opts['sms_provider'] ?? 'twilio'); ?>>Twilio</option> 576 </select> 577 </div> 578 <div class="bc-config-row"> 579 <label>Account SID</label> 580 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_account_sid]" value="<?php echo esc_attr($opts['sms_account_sid'] ?? ''); ?>" placeholder="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> 581 </div> 582 <div class="bc-config-row"> 583 <label>Auth Token</label> 584 <input type="password" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_auth_token]" value="<?php echo esc_attr($opts['sms_auth_token'] ?? ''); ?>" placeholder="your_auth_token"> 585 </div> 586 <div class="bc-config-row"> 587 <label>SMS Number (with country code)</label> 588 <input type="text" class="bc-input bc-input-full" name="<?php echo $this->option_name; ?>[sms_from]" value="<?php echo esc_attr($opts['sms_from'] ?? ''); ?>" placeholder="+15017122661"> 589 </div> 590 </div> 591 592 <!-- General Settings --> 593 <div class="bc-section-title">⚙️ General Settings</div> 594 595 <div class="bc-row bc-row-flex"> 340 596 <div class="bc-label"> 341 597 <strong>OTP Validity</strong> 342 598 <span>Minutes before code expires</span> 343 599 </div> 344 <input type="number" class="bc-input" style="width: 80px;" name="<?php echo $this->option_name; ?>[otp_validity]" value="<?php echo esc_attr($opts['otp_validity'] ?? 10); ?>" >600 <input type="number" class="bc-input" style="width: 80px;" name="<?php echo $this->option_name; ?>[otp_validity]" value="<?php echo esc_attr($opts['otp_validity'] ?? 10); ?>" min="1" max="30"> 345 601 </div> 346 602 … … 349 605 </div> 350 606 </div> 607 608 <script> 609 (function($) { 610 $(document).ready(function() { 611 // Recipient mode toggle 612 function toggleRecipientFields() { 613 const mode = $('#recipient-mode').val(); 614 $('#manager-email-row, #selected-users-row').hide(); 615 if (mode === 'manager') { 616 $('#manager-email-row').show(); 617 } else if (mode === 'selected') { 618 $('#selected-users-row').show(); 619 } 620 } 621 622 $('#recipient-mode').on('change', toggleRecipientFields); 623 toggleRecipientFields(); 624 625 // Delivery method toggle 626 function toggleDeliveryConfigs() { 627 $('.bc-config-section').removeClass('active'); 628 $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]:checked').each(function() { 629 const method = $(this).val(); 630 $('#config-' + method).addClass('active'); 631 }); 632 } 633 634 $('input[name="<?php echo $this->option_name; ?>[delivery_methods][]"]').on('change', toggleDeliveryConfigs); 635 toggleDeliveryConfigs(); 636 }); 637 })(jQuery); 638 </script> 351 639 <?php 352 640 } -
basecloud-shield/trunk/package.json
r3442297 r3442344 1 1 { 2 2 "name": "basecloud-shield", 3 "version": "1. 0.0",3 "version": "1.1.0", 4 4 "description": "WordPress 2FA Security Plugin - Build and deployment scripts", 5 5 "scripts": { … … 25 25 "authentication", 26 26 "sendgrid", 27 "whatsapp", 28 "sms", 29 "twilio", 27 30 "login-protection" 28 31 ], -
basecloud-shield/trunk/readme.txt
r3442333 r3442344 1 1 === BaseCloud Shield === 2 2 Contributors: basecloud 3 Tags: 2fa, security, otp, login protection, sendgrid 3 Tags: 2fa, security, otp, login protection, sendgrid, whatsapp, sms, twilio 4 4 Requires at least: 5.0 5 5 Tested up to: 6.9 6 Stable tag: 1. 0.16 Stable tag: 1.2.0 7 7 Requires PHP: 7.4 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Enterprise-grade Two-Factor Authentication (2FA) with support for standard Email, SendGrid API, and BaseCloud CRM Webhooks.11 Enterprise-grade Two-Factor Authentication (2FA) with support for Email, SendGrid API, Webhooks, WhatsApp, and SMS delivery. 12 12 13 13 == Description == … … 18 18 19 19 * **Plug & Play:** Works immediately using standard WordPress email delivery. 20 * **Central Manager Routing:** Option to route ALL login OTPs to a single "Manager Email" address (great for agencies managing client sites). 20 * **Multi-Recipient System:** Send OTPs to the logging-in user, a manager email, or selected users. 21 * **Multi-Channel Delivery:** Choose multiple delivery methods simultaneously (Email, SendGrid, WhatsApp, SMS, Webhook). 22 * **WhatsApp Integration:** Send OTPs directly via WhatsApp using Twilio API. 23 * **SMS Integration:** Deliver OTPs via SMS using Twilio API. 21 24 * **SendGrid API V3:** Native integration for high-deliverability emails. 22 * ** BaseCloud CRM Integration:** Connects to BaseCloud Webhooks for advanced automation flows (SMS, WhatsApp, etc).25 * **Webhook Support:** Connect to custom webhooks for advanced automation flows. 23 26 * **Secure OTPs:** 6-digit one-time passwords that expire automatically. 24 27 * **Browser Trust:** "Remember this device" functionality to reduce friction for authorized users. … … 56 59 **Important**: You must have a SendGrid account and API key to use this feature. You are responsible for complying with SendGrid's terms of service and ensuring proper data handling practices. 57 60 58 ** BaseCloud CRM Webhook(Optional)**61 **Twilio API for WhatsApp & SMS (Optional)** 59 62 60 If you select "BaseCloud CRM Webhook" as your delivery method, the plugin will send login notification data to a webhook URL you configure. 63 If you select "WhatsApp" or "SMS" as delivery methods, the plugin will send data to Twilio's API to deliver one-time password codes. 64 65 * **Service**: Twilio 66 * **What it's used for**: Sending two-factor authentication codes via WhatsApp and/or SMS 67 * **When data is sent**: Every time a user attempts to log in and 2FA is enabled with WhatsApp/SMS selected 68 * **Data sent**: 69 - Recipient phone number (from user meta field 'billing_phone') 70 - Sender phone number (WhatsApp number or SMS number configured in settings) 71 - Site name 72 - Username attempting to log in 73 - 6-digit one-time password code 74 - Message body 75 * **API Endpoint**: https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json 76 * **Terms of Service**: https://www.twilio.com/legal/tos 77 * **Privacy Policy**: https://www.twilio.com/legal/privacy 78 79 **Important**: You must have a Twilio account with WhatsApp and/or SMS capabilities enabled. Phone numbers must be stored in user meta (field: 'billing_phone'). You are responsible for complying with Twilio's terms of service. 80 81 **Custom Webhook (Optional)** 82 83 If you select "Webhook" as a delivery method, the plugin will send login notification data to a webhook URL you configure. 61 84 62 85 * **Service**: Custom webhook endpoint (configured by you) 63 * **What it's used for**: Sending login notifications to external systems for custom processing (SMS, WhatsApp, logging, etc.)86 * **What it's used for**: Sending login notifications to external systems for custom processing 64 87 * **When data is sent**: Every time a user attempts to log in and 2FA is enabled 65 88 * **Data sent**: … … 68 91 - User email address 69 92 - 6-digit one-time password code 93 - Recipient information array 70 94 - Timestamp of login attempt 71 95 * **Endpoint**: User-configured webhook URL … … 95 119 == Changelog == 96 120 121 = 1.2.0 = 122 **Major Feature Release - Multi-Recipient & Multi-Channel Delivery** 123 124 • Added Multi-Recipient System with 3 modes: 125 - Send to Logging-in User (default) 126 - Send to Manager Email (centralized notifications) 127 - Send to Selected Users (choose specific users from your site) 128 • Added Multi-Channel Delivery - select multiple delivery methods simultaneously 129 • Added WhatsApp integration via Twilio API 130 • Added SMS integration via Twilio API 131 • Enhanced UI with organized sections and dynamic form fields 132 • User selection interface with multi-select dropdown 133 • Auto-detection of all WordPress users on the site 134 • Smart routing system sends OTP to all selected recipients via all selected methods 135 • Phone number retrieval from user meta (billing_phone field) 136 • Improved settings panel layout with collapsible configuration sections 137 • Each delivery method now has dedicated configuration area 138 • Backward compatible with existing configurations 139 140 = 1.1.0 = 141 **Internal Development Version** 142 143 • Pre-release testing version 144 97 145 = 1.0.1 = 98 ** Release Update**146 **UI Improvements** 99 147 100 • Bug fixes and improvements 148 • Updated labels and placeholders to be more generic for broader use 149 • Changed "BaseCloud CRM Webhook" to "Webhook" in delivery method options 150 • Removed BaseCloud-specific email placeholders for wider audience compatibility 101 151 • Updated version for deployment 102 152
Note: See TracChangeset
for help on using the changeset viewer.