feat(security): restrict /app/queue and live OPD token feed to authorized hospital IPs (WASA CWE-200 remediation)
This commit is contained in:
parent
7547d00bb4
commit
0020d0676c
|
|
@ -1467,6 +1467,7 @@ $routes->group('setting', static function($routes) {
|
||||||
$routes->post('admin/opd-queue-tv/save', 'Setting\\OpdQueueSetting::save');
|
$routes->post('admin/opd-queue-tv/save', 'Setting\\OpdQueueSetting::save');
|
||||||
$routes->post('admin/opd-queue-tv/upload-banner', 'Setting\\OpdQueueSetting::uploadBanner');
|
$routes->post('admin/opd-queue-tv/upload-banner', 'Setting\\OpdQueueSetting::uploadBanner');
|
||||||
$routes->post('admin/opd-queue-tv/delete-banner', 'Setting\\OpdQueueSetting::deleteBanner');
|
$routes->post('admin/opd-queue-tv/delete-banner', 'Setting\\OpdQueueSetting::deleteBanner');
|
||||||
|
$routes->post('admin/opd-queue-tv/save-ip-security', 'Setting\\OpdQueueSetting::saveIpSecurity');
|
||||||
$routes->get('admin/medical-store', 'Setting\\MedicalStoreAdmin::index');
|
$routes->get('admin/medical-store', 'Setting\\MedicalStoreAdmin::index');
|
||||||
$routes->post('admin/medical-store/save', 'Setting\\MedicalStoreAdmin::save');
|
$routes->post('admin/medical-store/save', 'Setting\\MedicalStoreAdmin::save');
|
||||||
$routes->post('admin/medical-store/delete', 'Setting\\MedicalStoreAdmin::delete');
|
$routes->post('admin/medical-store/delete', 'Setting\\MedicalStoreAdmin::delete');
|
||||||
|
|
|
||||||
|
|
@ -872,6 +872,12 @@ class DoctorApi extends BaseController
|
||||||
*/
|
*/
|
||||||
public function queueIndex()
|
public function queueIndex()
|
||||||
{
|
{
|
||||||
|
if (! $this->isQueueAccessAllowed()) {
|
||||||
|
return $this->response
|
||||||
|
->setStatusCode(403)
|
||||||
|
->setBody($this->renderQueueAccessDeniedHtml());
|
||||||
|
}
|
||||||
|
|
||||||
$pwaPath = FCPATH . 'App/Queue/index.html';
|
$pwaPath = FCPATH . 'App/Queue/index.html';
|
||||||
if (file_exists($pwaPath)) {
|
if (file_exists($pwaPath)) {
|
||||||
return $this->response->setBody(file_get_contents($pwaPath));
|
return $this->response->setBody(file_get_contents($pwaPath));
|
||||||
|
|
@ -907,12 +913,246 @@ class DoctorApi extends BaseController
|
||||||
return $this->response->setStatusCode(404);
|
return $this->response->setStatusCode(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies if the incoming request is authorized to access the OPD TV Queue Display / API.
|
||||||
|
* Access is allowed if:
|
||||||
|
* 1. User has an active authenticated HMS session (CodeIgniter Shield / user_id)
|
||||||
|
* 2. TV_QUEUE_IP_RESTRICTION_ENABLED setting is set to '0' (disabled)
|
||||||
|
* 3. Request origin matches allowed IPs/subnets (TV_QUEUE_ALLOWED_IPS)
|
||||||
|
*/
|
||||||
|
private function isQueueAccessAllowed(): bool
|
||||||
|
{
|
||||||
|
// 1. Logged in staff / administrator can always view
|
||||||
|
if ((function_exists('auth') && auth()->loggedIn()) || (session()->has('user_id') && (int) session()->get('user_id') > 0)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = db_connect();
|
||||||
|
$enabledRow = $db->table('hospital_setting')
|
||||||
|
->select('s_value')
|
||||||
|
->where('s_name', 'TV_QUEUE_IP_RESTRICTION_ENABLED')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
$restrictionEnabled = ($enabledRow['s_value'] ?? '1') === '1';
|
||||||
|
if (! $restrictionEnabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Resolve client IP
|
||||||
|
$clientIp = trim((string) $this->request->getIPAddress());
|
||||||
|
if ($clientIp === '' || $clientIp === '0.0.0.0') {
|
||||||
|
$clientIp = trim((string) ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fetch allowed IP rules
|
||||||
|
$allowedRow = $db->table('hospital_setting')
|
||||||
|
->select('s_value')
|
||||||
|
->where('s_name', 'TV_QUEUE_ALLOWED_IPS')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
$defaultAllowed = "127.0.0.1\n::1\n10.*\n192.168.*\n172.16.*\n103.251.140.182";
|
||||||
|
$allowedIpsRaw = !empty($allowedRow['s_value']) ? (string) $allowedRow['s_value'] : $defaultAllowed;
|
||||||
|
|
||||||
|
$rules = preg_split('/[\r\n,]+/', $allowedIpsRaw, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
$rule = trim($rule);
|
||||||
|
if ($rule === '') continue;
|
||||||
|
|
||||||
|
if ($this->matchIpRule($clientIp, $rule)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a client IP matches an individual IP rule (exact, wildcard, or CIDR).
|
||||||
|
*/
|
||||||
|
private function matchIpRule(string $clientIp, string $rule): bool
|
||||||
|
{
|
||||||
|
if ($clientIp === $rule) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule === 'localhost' && in_array($clientIp, ['127.0.0.1', '::1'], true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array(strtolower($rule), ['intranet', 'private', 'lan'], true)) {
|
||||||
|
return $this->isHospitalIntranet($clientIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check wildcard (e.g. 192.168.*, 10.*, 103.251.*)
|
||||||
|
if (strpos($rule, '*') !== false) {
|
||||||
|
if (fnmatch($rule, $clientIp)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check CIDR (e.g. 192.168.0.0/16 or 10.0.0.0/8)
|
||||||
|
if (strpos($rule, '/') !== false) {
|
||||||
|
[$subnet, $bits] = explode('/', $rule, 2);
|
||||||
|
$bits = (int) $bits;
|
||||||
|
$ipLong = ip2long($clientIp);
|
||||||
|
$subnetLong = ip2long(trim($subnet));
|
||||||
|
if ($ipLong !== false && $subnetLong !== false && $bits >= 0 && $bits <= 32) {
|
||||||
|
$mask = $bits === 0 ? 0 : (~0 << (32 - $bits));
|
||||||
|
if (($ipLong & $mask) === ($subnetLong & $mask)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a branded 403 Forbidden screen when /app/queue is accessed externally.
|
||||||
|
*/
|
||||||
|
private function renderQueueAccessDeniedHtml(): string
|
||||||
|
{
|
||||||
|
$clientIp = esc((string) $this->request->getIPAddress());
|
||||||
|
$loginUrl = base_url('login');
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>403 Forbidden • OPD Queue TV Display</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #0b132b 0%, #1c2541 50%, #1e293b 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
color: #f8fafc;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.security-card {
|
||||||
|
max-width: 580px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(15, 23, 42, 0.85);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
padding: 2.5rem 2rem;
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.badge-terminal {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
color: #f87171;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 0.35rem 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.icon-circle {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
border: 2px solid rgba(239, 68, 68, 0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
color: #ef4444;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.ip-box {
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
border: 1px dashed rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.btn-login {
|
||||||
|
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.7rem 1.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: transform 0.15s ease;
|
||||||
|
}
|
||||||
|
.btn-login:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="security-card">
|
||||||
|
<div class="badge-terminal">
|
||||||
|
<i class="bi bi-shield-lock-fill"></i> Restricted Hospital Terminal (CWE-200)
|
||||||
|
</div>
|
||||||
|
<div class="icon-circle">
|
||||||
|
<i class="bi bi-display"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="fw-bold mb-2">OPD TV Display Access Restricted</h3>
|
||||||
|
<p class="text-secondary small mb-3">
|
||||||
|
This live queue & token tracker display is restricted to authorized hospital waiting rooms, local intranet (LAN), and secure VPN terminals.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="ip-box">
|
||||||
|
Client IP: <span class="text-warning">{$clientIp}</span> • Status: <span class="text-danger fw-bold">Unauthorized</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted small" style="font-size: 13px;">
|
||||||
|
If you are hospital staff setting up a waiting room display, please add this IP to the whitelist in <strong>Admin Panel > OPD Queue TV Settings</strong> or log in using your staff credentials.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-center gap-2 mt-4">
|
||||||
|
<a href="{$loginUrl}" class="btn-login">
|
||||||
|
<i class="bi bi-box-arrow-in-right"></i> Staff HMS Login
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET api/v1/opd/queue/live
|
* GET api/v1/opd/queue/live
|
||||||
* Returns real-time OPD Tokens & Hospital Showcase Ads for TV Queue Display
|
* Returns real-time OPD Tokens & Hospital Showcase Ads for TV Queue Display
|
||||||
*/
|
*/
|
||||||
public function liveQueueDisplay()
|
public function liveQueueDisplay()
|
||||||
{
|
{
|
||||||
|
if (! $this->isQueueAccessAllowed()) {
|
||||||
|
return $this->response
|
||||||
|
->setStatusCode(403)
|
||||||
|
->setJSON([
|
||||||
|
'status' => 0,
|
||||||
|
'error' => 'Forbidden',
|
||||||
|
'message' => 'Access Restricted: OPD Live Queue feed is only accessible from authorized hospital terminals and networks (WASA CWE-200 Remediation).',
|
||||||
|
'client_ip' => (string) $this->request->getIPAddress(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$db = db_connect();
|
$db = db_connect();
|
||||||
$todayStart = date('Y-m-d') . ' 00:00:00';
|
$todayStart = date('Y-m-d') . ' 00:00:00';
|
||||||
$tomorrowStart = date('Y-m-d', strtotime('+1 day')) . ' 00:00:00';
|
$tomorrowStart = date('Y-m-d', strtotime('+1 day')) . ' 00:00:00';
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,20 @@ class OpdQueueSetting extends BaseController
|
||||||
$customAds = json_decode($adsJson, true) ?: [];
|
$customAds = json_decode($adsJson, true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ipRestrictionEnabled = $this->getSetting('TV_QUEUE_IP_RESTRICTION_ENABLED', '1');
|
||||||
|
$defaultAllowedIps = "127.0.0.1\n::1\n10.*\n192.168.*\n172.16.*\n103.251.140.182";
|
||||||
|
$allowedIps = $this->getSetting('TV_QUEUE_ALLOWED_IPS', $defaultAllowedIps);
|
||||||
|
$currentClientIp = (string) $this->request->getIPAddress();
|
||||||
|
|
||||||
return view('Setting/Admin/opd_queue_setting', [
|
return view('Setting/Admin/opd_queue_setting', [
|
||||||
'footer_ticker' => $tickerText,
|
'footer_ticker' => $tickerText,
|
||||||
'left_ad_text' => $leftAdText,
|
'left_ad_text' => $leftAdText,
|
||||||
'left_ad_image' => $leftAdImage,
|
'left_ad_image' => $leftAdImage,
|
||||||
'left_ad_enabled' => $leftAdEnabled,
|
'left_ad_enabled' => $leftAdEnabled,
|
||||||
'custom_ads' => $customAds
|
'custom_ads' => $customAds,
|
||||||
|
'ip_restriction_enabled' => $ipRestrictionEnabled,
|
||||||
|
'allowed_ips' => $allowedIps,
|
||||||
|
'current_client_ip' => $currentClientIp,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,6 +156,21 @@ class OpdQueueSetting extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveIpSecurity()
|
||||||
|
{
|
||||||
|
$post = $this->request->getPost();
|
||||||
|
$enabled = isset($post['ip_restriction_enabled']) && $post['ip_restriction_enabled'] === '1' ? '1' : '0';
|
||||||
|
$allowedIps = trim((string) ($post['allowed_ips'] ?? ''));
|
||||||
|
|
||||||
|
$this->setSetting('TV_QUEUE_IP_RESTRICTION_ENABLED', $enabled);
|
||||||
|
$this->setSetting('TV_QUEUE_ALLOWED_IPS', $allowedIps);
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'status' => 1,
|
||||||
|
'message' => 'OPD Queue TV IP restriction settings updated successfully!'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private function getSetting(string $name, string $default = ''): string
|
private function getSetting(string $name, string $default = ''): string
|
||||||
{
|
{
|
||||||
$row = $this->db->table('hospital_setting')
|
$row = $this->db->table('hospital_setting')
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,64 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- TV Display Network & IP Security (WASA Compliance) -->
|
||||||
|
<div class="card shadow-sm border-0 mb-3">
|
||||||
|
<div class="card-header bg-dark text-white fw-bold d-flex justify-content-between align-items-center">
|
||||||
|
<span><i class="bi bi-shield-lock-fill me-2 text-warning"></i> TV Display Network & IP Security</span>
|
||||||
|
<span class="badge bg-warning text-dark"><i class="bi bi-shield-check me-1"></i>WASA Security</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body mt-3">
|
||||||
|
<form id="form_ip_security_settings" action="javascript:void(0)" onsubmit="saveIpSecuritySettings(event)">
|
||||||
|
<div class="alert alert-light border d-flex align-items-start gap-2 py-2 mb-3">
|
||||||
|
<i class="bi bi-info-circle-fill text-primary mt-1 fs-5"></i>
|
||||||
|
<div class="small text-muted">
|
||||||
|
<strong>Protection for <code>/app/queue</code> & live token feeds:</strong> Only whitelisted hospital local IPs (LAN), VPN addresses, or authorized static IPs can open the TV Display. All unauthorized public internet requests receive <strong>HTTP 403 Forbidden</strong> with zero doctor/queue data exposed.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check form-switch mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" name="ip_restriction_enabled" id="ip_restriction_enabled" value="1" <?= $ip_restriction_enabled === '1' ? 'checked' : '' ?>>
|
||||||
|
<label class="form-check-label fw-bold" for="ip_restriction_enabled">
|
||||||
|
Enforce IP / VPN Restriction for TV Display
|
||||||
|
</label>
|
||||||
|
<small class="d-block text-muted">When enabled, only allowed IPs or logged-in staff can access <code>/app/queue</code> and live OPD data.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||||
|
<label class="form-label fw-bold mb-0">Allowed IP Addresses & Subnets</label>
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm py-0 px-2" style="font-size: 12px;" onclick="addCurrentClientIp()">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i> Add My IP (<?= esc($current_client_ip) ?>)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<textarea class="form-control font-monospace" name="allowed_ips" id="allowed_ips_textarea" rows="4" placeholder="127.0.0.1 192.168.* 10.* 103.251.140.182" style="font-size: 13px;"><?= esc($allowed_ips) ?></textarea>
|
||||||
|
<div class="form-text small mt-1">
|
||||||
|
Enter one IP or subnet per line or separated by commas.<br>
|
||||||
|
• Exact: <code>103.251.140.182</code>, <code>127.0.0.1</code><br>
|
||||||
|
• Wildcards: <code>192.168.*</code>, <code>10.*</code>, <code>172.16.*</code><br>
|
||||||
|
• Subnet CIDR: <code>10.8.0.0/24</code>, <code>192.168.1.0/24</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertIpRule('192.168.*')">
|
||||||
|
+ Hospital LAN (192.168.*)
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertIpRule('10.*')">
|
||||||
|
+ Hospital VPN / Intranet (10.*)
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertIpRule('127.0.0.1\n::1')">
|
||||||
|
+ Localhost Only
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" onclick="saveIpSecuritySettings(event)" class="btn btn-dark fw-bold px-4">
|
||||||
|
<i class="bi bi-shield-check text-warning me-1"></i> Save IP Security Settings
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Side: Showcase & Ad Banners Management -->
|
<!-- Right Side: Showcase & Ad Banners Management -->
|
||||||
|
|
@ -232,4 +290,53 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var myCurrentIp = '<?= esc($current_client_ip) ?>';
|
||||||
|
|
||||||
|
function addCurrentClientIp() {
|
||||||
|
if (!myCurrentIp) return;
|
||||||
|
insertIpRule(myCurrentIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertIpRule(rule) {
|
||||||
|
var el = document.getElementById('allowed_ips_textarea');
|
||||||
|
if (!el) return;
|
||||||
|
var cur = el.value.trim();
|
||||||
|
var rules = rule.split('\n');
|
||||||
|
rules.forEach(function(r) {
|
||||||
|
r = r.trim();
|
||||||
|
if (r !== '' && cur.indexOf(r) === -1) {
|
||||||
|
cur = cur === '' ? r : cur + '\n' + r;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
el.value = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveIpSecuritySettings(e) {
|
||||||
|
if (e && e.preventDefault) e.preventDefault();
|
||||||
|
var form = document.getElementById('form_ip_security_settings');
|
||||||
|
if (!form) return false;
|
||||||
|
var formData = new FormData(form);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '<?= base_url('setting/admin/opd-queue-tv/save-ip-security') ?>',
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(res) {
|
||||||
|
if (res.status === 1) {
|
||||||
|
alert(res.message);
|
||||||
|
refreshAdminSubPage();
|
||||||
|
} else {
|
||||||
|
alert(res.message || 'Error saving IP security settings');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Error saving IP security settings');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user