fix(security): implement WASA compliance hardening and vulnerability remediation
This commit is contained in:
parent
ec377dafcd
commit
81f3501157
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -141,6 +141,7 @@ NiceAdmin-pro/
|
|||
|
||||
public/uploads/*
|
||||
!public/uploads/.gitkeep
|
||||
!public/uploads/.htaccess
|
||||
|
||||
# Always allow branding assets to be committed when present
|
||||
!public/assets/
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ class App extends BaseConfig
|
|||
* secure, the user will be redirected to a secure version of the page
|
||||
* and the HTTP Strict Transport Security (HSTS) header will be set.
|
||||
*/
|
||||
public bool $forceGlobalSecureRequests = false;
|
||||
public bool $forceGlobalSecureRequests = (ENVIRONMENT === 'production');
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -56,6 +56,15 @@ class Cookie extends BaseConfig
|
|||
*/
|
||||
public bool $secure = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (ENVIRONMENT === 'production' || (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')) {
|
||||
$this->secure = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie HTTPOnly
|
||||
|
|
|
|||
|
|
@ -74,25 +74,23 @@ class Filters extends BaseFilters
|
|||
public array $globals = [
|
||||
'before' => [
|
||||
'singleSession',
|
||||
'invalidchars',
|
||||
'csrf' => [
|
||||
'except' => [
|
||||
'api/*',
|
||||
'AbdmGateway/*',
|
||||
'AbdmHiu/*',
|
||||
'HealthplixCallback/*',
|
||||
'HealthplixGateway/*',
|
||||
'stt-proxy',
|
||||
'stt-proxy/*',
|
||||
],
|
||||
],
|
||||
// 'honeypot',
|
||||
// 'csrf',
|
||||
// 'invalidchars',
|
||||
],
|
||||
'after' => [
|
||||
'toolbar' => [
|
||||
'except' => [
|
||||
'Opd/*',
|
||||
'Opd_prescription/*',
|
||||
'billing/patient/abdm_*',
|
||||
'billing/patient/abdm_*/*',
|
||||
'AbdmHiu',
|
||||
'AbdmHiu/*',
|
||||
'AbdmGateway/*',
|
||||
'api/*',
|
||||
],
|
||||
],
|
||||
'secureheaders',
|
||||
// 'honeypot',
|
||||
// 'secureheaders',
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class Security extends BaseConfig
|
|||
*
|
||||
* Randomize the CSRF Token for added security.
|
||||
*/
|
||||
public bool $tokenRandomize = false;
|
||||
public bool $tokenRandomize = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class Session extends BaseConfig
|
|||
* when auto-regenerating the session ID. When set to FALSE, the data
|
||||
* will be later deleted by the garbage collector.
|
||||
*/
|
||||
public bool $regenerateDestroy = false;
|
||||
public bool $regenerateDestroy = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -33,11 +33,27 @@ class AbdmFhirInspector extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
private function ensureAuthenticated()
|
||||
{
|
||||
if (! function_exists('auth') || ! auth()->loggedIn()) {
|
||||
if ($this->request->isAJAX() || str_contains((string) $this->request->getHeaderLine('Accept'), 'application/json')) {
|
||||
return $this->response->setStatusCode(401)->setJSON(['ok' => 0, 'error' => 'Authentication required']);
|
||||
}
|
||||
return redirect()->to(base_url('login'))->with('error', 'Please log in to access the FHIR Inspector.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main page view for ABHA Filter & FHIR Inspector
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$filterAbha = trim((string) ($this->request->getGet('abha_address') ?? $this->request->getGet('abha') ?? ''));
|
||||
$patientId = (int) ($this->request->getGet('patient_id') ?? $this->request->getGet('id') ?? 0);
|
||||
$uhid = trim((string) ($this->request->getGet('uhid') ?? ''));
|
||||
|
|
@ -59,6 +75,10 @@ class AbdmFhirInspector extends BaseController
|
|||
*/
|
||||
public function patientData()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$patientId = (int) ($this->request->getGet('patient_id') ?? 0);
|
||||
$abhaAddress = trim((string) ($this->request->getGet('abha_address') ?? $this->request->getGet('abha') ?? ''));
|
||||
$pCode = trim((string) ($this->request->getGet('p_code') ?? $this->request->getGet('uhid') ?? ''));
|
||||
|
|
@ -87,6 +107,10 @@ class AbdmFhirInspector extends BaseController
|
|||
*/
|
||||
public function gatewayCheck()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$abha = trim((string) ($this->request->getGet('abha_address') ?? $this->request->getGet('abha') ?? ''));
|
||||
if ($abha === '') {
|
||||
return $this->response->setJSON(['ok' => 0, 'error_text' => 'ABHA address is required']);
|
||||
|
|
@ -131,6 +155,10 @@ class AbdmFhirInspector extends BaseController
|
|||
*/
|
||||
public function previewBundle()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$ref = trim((string) ($this->request->getGet('ref') ?? $this->request->getPost('ref') ?? $_GET['ref'] ?? $_REQUEST['ref'] ?? ''));
|
||||
$patientId = (int) ($this->request->getGet('patient_id') ?? $this->request->getPost('patient_id') ?? $_GET['patient_id'] ?? $_REQUEST['patient_id'] ?? 0);
|
||||
$abha = trim((string) ($this->request->getGet('abha') ?? $this->request->getPost('abha') ?? $_GET['abha'] ?? $_REQUEST['abha'] ?? ''));
|
||||
|
|
|
|||
|
|
@ -104,12 +104,29 @@ class DoctorApi extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
$clientIp = (string) $this->request->getIPAddress();
|
||||
$rateKey = 'doc_pin_' . md5($clientIp . '_' . $docId);
|
||||
$cache = service('cache');
|
||||
$failedAttempts = (int) ($cache->get($rateKey) ?? 0);
|
||||
if ($failedAttempts >= 5) {
|
||||
return $this->response->setStatusCode(429)->setJSON([
|
||||
'status' => 0,
|
||||
'message' => 'Too many failed PIN attempts. Account temporarily locked for 10 minutes.',
|
||||
]);
|
||||
}
|
||||
|
||||
$isValid = password_verify($pin, $hashedPin) || ($pin === $hashedPin);
|
||||
|
||||
if (! $isValid) {
|
||||
$cache->save($rateKey, $failedAttempts + 1, 600);
|
||||
return $this->response->setStatusCode(401)->setJSON(['status' => 0, 'message' => 'Incorrect Security PIN']);
|
||||
}
|
||||
|
||||
$cache->delete($rateKey);
|
||||
if (session_status() === PHP_SESSION_ACTIVE || ! session_id()) {
|
||||
session()->set('pwa_auth_doctor_id', (int) $doctorRow->id);
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 1,
|
||||
'message' => 'Authentication successful',
|
||||
|
|
|
|||
|
|
@ -150,12 +150,29 @@ class NursingApi extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
$clientIp = (string) $this->request->getIPAddress();
|
||||
$rateKey = 'nurse_pin_' . md5($clientIp . '_' . ($nurseRow['id'] ?? 0));
|
||||
$cache = service('cache');
|
||||
$failedAttempts = (int) ($cache->get($rateKey) ?? 0);
|
||||
if ($failedAttempts >= 5) {
|
||||
return $this->response->setStatusCode(429)->setJSON([
|
||||
'status' => 0,
|
||||
'message' => 'Too many failed PIN attempts. Account temporarily locked for 10 minutes.',
|
||||
]);
|
||||
}
|
||||
|
||||
$isValid = password_verify($pin, $hashedPin) || ($pin === $hashedPin);
|
||||
|
||||
if (! $isValid) {
|
||||
$cache->save($rateKey, $failedAttempts + 1, 600);
|
||||
return $this->response->setStatusCode(401)->setJSON(['status' => 0, 'message' => 'Incorrect Security PIN']);
|
||||
}
|
||||
|
||||
$cache->delete($rateKey);
|
||||
if (session_status() === PHP_SESSION_ACTIVE || ! session_id()) {
|
||||
session()->set('pwa_auth_nurse_id', (int) $nurseRow['id']);
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 1,
|
||||
'message' => 'Authentication successful',
|
||||
|
|
|
|||
|
|
@ -706,9 +706,9 @@ class CaseMaster extends BaseController
|
|||
$invoiceId = (int) $invoiceItem[0]->inv_master_id;
|
||||
}
|
||||
|
||||
$data['invoiceDetails'] = $this->db->query('select i.*,t.group_desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id=' . $invoiceId)->getResult();
|
||||
$data['invoiceDetails'] = $this->db->query('select i.*,t.group_desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id = ?', [$invoiceId])->getResult();
|
||||
$data['invoiceGtotal'] = $this->db->table('invoice_item')->selectSum('item_amount', 'Gtotal')->where('inv_master_id', $invoiceId)->get()->getResult();
|
||||
$data['invoice_master'] = $this->db->query("select *,(case payment_mode when 1 Then 'Cash' when 2 then 'Bank Card' when 3 then 'IPD Credit' when 4 then 'Org. Credit' else 'Pending' end) as Payment_type_str from invoice_master where id=" . $invoiceId)->getResult();
|
||||
$data['invoice_master'] = $this->db->query("select *,(case payment_mode when 1 Then 'Cash' when 2 then 'Bank Card' when 3 then 'IPD Credit' when 4 then 'Org. Credit' else 'Pending' end) as Payment_type_str from invoice_master where id = ?", [$invoiceId])->getResult();
|
||||
|
||||
$discountAmount = (float) ($data['invoice_master'][0]->discount_amount ?? 0);
|
||||
$correctionAmount = (float) ($data['invoice_master'][0]->correction_amount ?? 0);
|
||||
|
|
@ -1131,18 +1131,18 @@ class CaseMaster extends BaseController
|
|||
$data['MedInvoice'] = $this->db->query('select sum(m.net_amount) as Med_Amount,o.id ,count(m.id) as no_inv
|
||||
from invoice_med_master m join ipd_master p join organization_case_master o
|
||||
on m.ipd_id=p.id and p.case_id=o.id and m.ipd_credit=1 and m.ipd_credit_type=1
|
||||
where o.id=' . $caseid . ' group by o.id')->getResult();
|
||||
where o.id = ? group by o.id', [$caseid])->getResult();
|
||||
|
||||
$data['ipd_package'] = $this->db->query('SELECT p.* FROM ipd_package p JOIN ipd_master m ON p.ipd_id=m.id WHERE m.case_id=' . $caseid)->getResult();
|
||||
$data['ipd_package'] = $this->db->query('SELECT p.* FROM ipd_package p JOIN ipd_master m ON p.ipd_id=m.id WHERE m.case_id = ?', [$caseid])->getResult();
|
||||
|
||||
if (count($data['MedInvoice']) === 0) {
|
||||
$data['MedInvoice'] = $this->db->query('select sum(m.net_amount) as Med_Amount,o.id ,count(m.id) as no_inv
|
||||
from invoice_med_master m join organization_case_master o
|
||||
on m.case_id=o.id and m.case_credit=1
|
||||
where o.id=' . $caseid . ' group by o.id')->getResult();
|
||||
where o.id = ? group by o.id', [$caseid])->getResult();
|
||||
}
|
||||
|
||||
$ipdMaster = $this->db->query('select p.* from ipd_master p join organization_case_master o on p.case_id=o.id where o.id=' . $caseid)->getResult();
|
||||
$ipdMaster = $this->db->query('select p.* from ipd_master p join organization_case_master o on p.case_id=o.id where o.id = ?', [$caseid])->getResult();
|
||||
|
||||
$tableHead = '<table border="1" cellpadding="1" cellspacing="0" style="width:100%">'
|
||||
. '<tr><th style="width:50px">Sr.No.</th><th style="width:100px">Date</th><th>Details of Expenditure</th><th colspan="2">Number or Quantity</th><th style="width:100px;align:right;">Rate</th><th style="width:100px;align:right;">Per</th><th style="width:100px;align:right;">Amount</th></tr>';
|
||||
|
|
@ -1284,10 +1284,10 @@ class CaseMaster extends BaseController
|
|||
|
||||
$orgId = (int) $data['org_info'][0]->id;
|
||||
|
||||
$data['ipd_info'] = $this->db->query("select * from v_ipd_list where org_id='" . $orgId . "' ")->getResult();
|
||||
$data['ipd_info'] = $this->db->query("select * from v_ipd_list where org_id = ?", [$orgId])->getResult();
|
||||
|
||||
$sql = "select *,if(gender=1,'Male','Female') as xgender from patient_master where id='" . $data['org_info'][0]->p_id . "' ";
|
||||
$data['person_info'] = $this->db->query($sql)->getResult();
|
||||
$sql = "select *,if(gender=1,'Male','Female') as xgender from patient_master where id = ?";
|
||||
$data['person_info'] = $this->db->query($sql, [$data['org_info'][0]->p_id])->getResult();
|
||||
$this->applyAge($data['person_info'], 'str_age');
|
||||
|
||||
return view('Invoice/org_case_payment', $data);
|
||||
|
|
@ -1295,17 +1295,17 @@ class CaseMaster extends BaseController
|
|||
|
||||
public function load_model_box(string $caseidcode)
|
||||
{
|
||||
$data['org_info'] = $this->db->query("select * from organization_case_master where case_id_code='" . $caseidcode . "' ")->getResult();
|
||||
$data['org_info'] = $this->db->query("select * from organization_case_master where case_id_code = ?", [$caseidcode])->getResult();
|
||||
if (empty($data['org_info'])) {
|
||||
return $this->response->setStatusCode(404)->setBody('Case not found');
|
||||
}
|
||||
|
||||
$orgId = (int) $data['org_info'][0]->id;
|
||||
|
||||
$data['ipd_info'] = $this->db->query("select * from v_ipd_list where org_id='" . $orgId . "' ")->getResult();
|
||||
$data['ipd_info'] = $this->db->query("select * from v_ipd_list where org_id = ?", [$orgId])->getResult();
|
||||
|
||||
$sql = "select *,if(gender=1,'Male','Female') as xgender from patient_master where id='" . $data['org_info'][0]->p_id . "' ";
|
||||
$data['person_info'] = $this->db->query($sql)->getResult();
|
||||
$sql = "select *,if(gender=1,'Male','Female') as xgender from patient_master where id = ?";
|
||||
$data['person_info'] = $this->db->query($sql, [$data['org_info'][0]->p_id])->getResult();
|
||||
$this->applyAge($data['person_info'], 'str_age');
|
||||
|
||||
return view('Invoice/org_case_payment', $data);
|
||||
|
|
@ -1340,7 +1340,7 @@ class CaseMaster extends BaseController
|
|||
|
||||
if (! empty($orgInfo)) {
|
||||
$patientId = (int) $orgInfo[0]->p_id;
|
||||
$personInfo = $this->db->query("select *,if(gender=1,'Male','Female') as xgender from patient_master where id='" . $patientId . "' ")->getResult();
|
||||
$personInfo = $this->db->query("select *,if(gender=1,'Male','Female') as xgender from patient_master where id = ?", [$patientId])->getResult();
|
||||
$this->applyAge($personInfo, 'str_age');
|
||||
$orgDate = $orgInfo[0]->insert_datetime;
|
||||
$patientName = $personInfo[0]->p_fname ?? '';
|
||||
|
|
@ -1403,7 +1403,7 @@ class CaseMaster extends BaseController
|
|||
|
||||
if (! empty($orgInfo)) {
|
||||
$patientId = (int) $orgInfo[0]->p_id;
|
||||
$personInfo = $this->db->query("select *,if(gender=1,'Male','Female') as xgender from patient_master where id='" . $patientId . "' ")->getResult();
|
||||
$personInfo = $this->db->query("select *,if(gender=1,'Male','Female') as xgender from patient_master where id = ?", [$patientId])->getResult();
|
||||
$this->applyAge($personInfo, 'str_age');
|
||||
$orgDate = $orgInfo[0]->insert_datetime;
|
||||
$patientName = $personInfo[0]->p_fname ?? '';
|
||||
|
|
|
|||
|
|
@ -512,15 +512,31 @@ class DoctorDocument extends BaseController
|
|||
}
|
||||
|
||||
$origName = $file->getClientName();
|
||||
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION) ?: 'pdf');
|
||||
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION) ?: '');
|
||||
$allowedExts = ['pdf', 'jpg', 'jpeg', 'png', 'webp'];
|
||||
if (! in_array($ext, $allowedExts, true)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Invalid file format. Only PDF, JPG, PNG, and WEBP files are allowed.',
|
||||
]);
|
||||
}
|
||||
|
||||
$mimeType = $file->getClientMimeType() ?: 'application/pdf';
|
||||
$allowedMimes = ['application/pdf', 'image/jpeg', 'image/png', 'image/webp'];
|
||||
if (! in_array($mimeType, $allowedMimes, true)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Invalid file type. Only PDF and image uploads are permitted.',
|
||||
]);
|
||||
}
|
||||
|
||||
$uploadDir = FCPATH . 'uploads' . DIRECTORY_SEPARATOR . 'nabh_ipd' . DIRECTORY_SEPARATOR;
|
||||
if (! is_dir($uploadDir)) {
|
||||
@mkdir($uploadDir, 0755, true);
|
||||
}
|
||||
|
||||
$newFileName = 'NABH_IPD_' . $ipdId . '_' . str_replace('-', '_', $nabhCategory) . '_' . date('Ymd_His') . '.' . $ext;
|
||||
$safeCategory = preg_replace('/[^A-Za-z0-9_]/', '_', $nabhCategory);
|
||||
$newFileName = 'NABH_IPD_' . $ipdId . '_' . $safeCategory . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
|
||||
$file->move($uploadDir, $newFileName);
|
||||
|
||||
$fileFullPath = $uploadDir . $newFileName;
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ class HealthplixCallback extends BaseController
|
|||
$expectedSecret = $this->resolveExpectedSecret();
|
||||
$providedSecret = trim((string) $this->request->getHeaderLine('X-Healthplix-Secret'));
|
||||
|
||||
if ($expectedSecret !== '' && ! hash_equals($expectedSecret, $providedSecret)) {
|
||||
if ($expectedSecret === '' || ! hash_equals($expectedSecret, $providedSecret)) {
|
||||
$this->logFetchEvent('error', 401, [
|
||||
'message' => 'Invalid callback secret',
|
||||
'message' => 'Missing or invalid callback secret',
|
||||
'payload_bytes' => strlen($rawBody),
|
||||
'payload_keys' => array_keys($payload),
|
||||
], 'Unauthorized callback request');
|
||||
|
|
|
|||
|
|
@ -6,8 +6,21 @@ use App\Libraries\HealthplixService;
|
|||
|
||||
class HealthplixGateway extends BaseController
|
||||
{
|
||||
private function ensureAuthenticated()
|
||||
{
|
||||
if (! function_exists('auth') || ! auth()->loggedIn()) {
|
||||
return $this->response->setStatusCode(401)->setJSON(['ok' => 0, 'error_text' => 'Authentication required']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function generateToken()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
|
@ -30,6 +43,10 @@ class HealthplixGateway extends BaseController
|
|||
|
||||
public function registerPatient()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
|
@ -61,6 +78,10 @@ class HealthplixGateway extends BaseController
|
|||
|
||||
public function bookAppointment()
|
||||
{
|
||||
if ($deny = $this->ensureAuthenticated()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ class OcasePathLap extends BaseController
|
|||
|
||||
public function showinvoice(int $invoiceId)
|
||||
{
|
||||
$sql = 'select i.*,t.desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id=' . $invoiceId;
|
||||
$data['invoiceDetails'] = $this->db->query($sql)->getResult();
|
||||
$sql = 'select i.*,t.desc from invoice_item i join hc_item_type t on i.item_type=t.itype_id where i.inv_master_id = ?';
|
||||
$data['invoiceDetails'] = $this->db->query($sql, [$invoiceId])->getResult();
|
||||
|
||||
$data['invoiceGtotal'] = $this->db->table('invoice_item')
|
||||
->selectSum('item_amount', 'Gtotal')
|
||||
|
|
@ -97,8 +97,8 @@ class OcasePathLap extends BaseController
|
|||
return $this->response->setStatusCode(404)->setBody('Invoice not found');
|
||||
}
|
||||
|
||||
$sql = 'select *,if(gender=1,\'Male\',\'Female\') as xgender from patient_master where id=' . (int) $data['invoice_master'][0]->attach_id;
|
||||
$data['patient_master'] = $this->db->query($sql)->getResult();
|
||||
$sql = 'select *,if(gender=1,\'Male\',\'Female\') as xgender from patient_master where id = ?';
|
||||
$data['patient_master'] = $this->db->query($sql, [(int) $data['invoice_master'][0]->attach_id])->getResult();
|
||||
$this->applyAge($data['patient_master']);
|
||||
|
||||
$insCompId = (int) $data['invoice_master'][0]->insurance_id;
|
||||
|
|
@ -119,7 +119,7 @@ class OcasePathLap extends BaseController
|
|||
->get()
|
||||
->getResult();
|
||||
|
||||
$data['payment_history'] = $this->db->query('select sum(if(credit_debit>0,amount*-1,amount)) as paid_amount from payment_history where payof_type=2 and payof_id=' . $invoiceId)->getResult();
|
||||
$data['payment_history'] = $this->db->query('select sum(if(credit_debit>0,amount*-1,amount)) as paid_amount from payment_history where payof_type=2 and payof_id = ?', [$invoiceId])->getResult();
|
||||
|
||||
$discountAmount = (float) $data['invoice_master'][0]->discount_amount;
|
||||
$totalAmount = (float) ($data['invoiceGtotal'][0]->Gtotal ?? 0);
|
||||
|
|
@ -378,7 +378,7 @@ class OcasePathLap extends BaseController
|
|||
->get()
|
||||
->getResult();
|
||||
|
||||
$data['payment_history'] = $this->db->query('select *,(case payment_mode when 1 Then \'cash\' when 2 then \'Bank Card\' when 3 then \'Org Credit\' else \'Pending\' end) as Payment_type_str from payment_history where payof_type=2 and payof_id=' . $invoiceId)->getResult();
|
||||
$data['payment_history'] = $this->db->query('select *,(case payment_mode when 1 Then \'cash\' when 2 then \'Bank Card\' when 3 then \'Org Credit\' else \'Pending\' end) as Payment_type_str from payment_history where payof_type=2 and payof_id = ?', [$invoiceId])->getResult();
|
||||
|
||||
return view('Invoice/Organization_invoice_print_v', $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3859,6 +3859,11 @@ class Patient extends BaseController
|
|||
|
||||
$file = $this->request->getFile('upload_file');
|
||||
if ($file && $file->isValid() && !$file->hasMoved()) {
|
||||
$ext = strtolower((string) ($file->getClientExtension() ?: pathinfo((string) $file->getName(), PATHINFO_EXTENSION)));
|
||||
$allowedExts = ['pdf', 'jpg', 'jpeg', 'png', 'webp', 'doc', 'docx'];
|
||||
if (! in_array($ext, $allowedExts, true)) {
|
||||
$message = 'Invalid file format. Upload PDF, image, or document only.';
|
||||
} else {
|
||||
$uploadPath = rtrim(FCPATH, '\\/') . '/uploads/patient';
|
||||
if (!is_dir($uploadPath)) {
|
||||
mkdir($uploadPath, 0755, true);
|
||||
|
|
@ -3879,6 +3884,7 @@ class Patient extends BaseController
|
|||
} else {
|
||||
$message = 'Unable to save the file.';
|
||||
}
|
||||
}
|
||||
} elseif ($file && !$file->isValid()) {
|
||||
$message = $file->getErrorString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,40 @@ use App\Libraries\SystemOperations;
|
|||
|
||||
class SystemOps extends BaseController
|
||||
{
|
||||
public function index(): string
|
||||
private function ensureAdminAccess()
|
||||
{
|
||||
if (! function_exists('auth') || ! auth()->loggedIn()) {
|
||||
if ($this->request->isAJAX() || str_contains((string) $this->request->getHeaderLine('Accept'), 'application/json')) {
|
||||
return $this->response->setStatusCode(401)->setJSON(['ok' => 0, 'error' => 'Authentication required']);
|
||||
}
|
||||
return redirect()->to(base_url('login'))->with('error', 'Authentication required.');
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
$isAllowed = false;
|
||||
if ($user && method_exists($user, 'inGroup')) {
|
||||
$isAllowed = $user->inGroup('superadmin', 'admin', 'developer');
|
||||
}
|
||||
if (! $isAllowed && $user && method_exists($user, 'can')) {
|
||||
$isAllowed = $user->can('admin.settings');
|
||||
}
|
||||
|
||||
if (! $isAllowed) {
|
||||
if ($this->request->isAJAX() || str_contains((string) $this->request->getHeaderLine('Accept'), 'application/json')) {
|
||||
return $this->response->setStatusCode(403)->setJSON(['ok' => 0, 'error' => 'Administrative privileges required']);
|
||||
}
|
||||
return $this->response->setStatusCode(403)->setBody('Access denied. Administrator privileges required.');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if ($deny = $this->ensureAdminAccess()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$ops = new SystemOperations();
|
||||
$data = [
|
||||
'status' => $ops->collectStatus(),
|
||||
|
|
@ -18,8 +50,12 @@ class SystemOps extends BaseController
|
|||
return view('Setting/Admin/system_ops', $data);
|
||||
}
|
||||
|
||||
public function panel(): string
|
||||
public function panel()
|
||||
{
|
||||
if ($deny = $this->ensureAdminAccess()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$ops = new SystemOperations();
|
||||
$data = [
|
||||
'status' => $ops->collectStatus(),
|
||||
|
|
@ -31,6 +67,10 @@ class SystemOps extends BaseController
|
|||
|
||||
public function updateDirect()
|
||||
{
|
||||
if ($deny = $this->ensureAdminAccess()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$ops = new SystemOperations();
|
||||
$result = $ops->runUpdateDirect();
|
||||
|
||||
|
|
@ -39,6 +79,10 @@ class SystemOps extends BaseController
|
|||
|
||||
public function diagnose()
|
||||
{
|
||||
if ($deny = $this->ensureAdminAccess()) {
|
||||
return $deny;
|
||||
}
|
||||
|
||||
$ops = new SystemOperations();
|
||||
$diag = $ops->getGitDiagnosticsForWeb();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,35 +6,35 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
<strong>Name :</strong><?=$person_info[0]->p_fname?> {<i><?=$person_info[0]->p_rname?></i>}
|
||||
<strong>/ Age :</strong><?=$person_info[0]->str_age?>
|
||||
<strong>/ Gender :</strong><?=$person_info[0]->xgender?>
|
||||
<strong>/ P Code :</strong><?=$person_info[0]->p_code?>
|
||||
<strong>Name :</strong><?= esc($person_info[0]->p_fname ?? '') ?> {<i><?= esc($person_info[0]->p_rname ?? '') ?></i>}
|
||||
<strong>/ Age :</strong><?= esc($person_info[0]->str_age ?? '') ?>
|
||||
<strong>/ Gender :</strong><?= esc($person_info[0]->xgender ?? '') ?>
|
||||
<strong>/ P Code :</strong><?= esc($person_info[0]->p_code ?? '') ?>
|
||||
</p>
|
||||
<input type="hidden" value="<?=$org_info[0]->id ?>" id="org_id" name="org_id" />
|
||||
<input type="hidden" value="<?= (int) ($org_info[0]->id ?? 0) ?>" id="org_id" name="org_id" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
<strong>Org Code :</strong><?=$org_info[0]->case_id_code?>
|
||||
<strong>/ Org. Name :</strong><?=$org_info[0]->insurance_company_name?>
|
||||
<strong>/ Card No :</strong><?=$org_info[0]->insurance_no?>
|
||||
<?=$org_info[0]->insurance_no_1?>
|
||||
<?=$org_info[0]->insurance_no_2?>
|
||||
<strong>Org Code :</strong><?= esc($org_info[0]->case_id_code ?? '') ?>
|
||||
<strong>/ Org. Name :</strong><?= esc($org_info[0]->insurance_company_name ?? '') ?>
|
||||
<strong>/ Card No :</strong><?= esc($org_info[0]->insurance_no ?? '') ?>
|
||||
<?= esc($org_info[0]->insurance_no_1 ?? '') ?>
|
||||
<?= esc($org_info[0]->insurance_no_2 ?? '') ?>
|
||||
</p>
|
||||
<input type="hidden" value="<?=$org_info[0]->id ?>" id="org_id" name="org_id" />
|
||||
<input type="hidden" value="<?= (int) ($org_info[0]->id ?? 0) ?>" id="org_id" name="org_id" />
|
||||
</div>
|
||||
</div>
|
||||
<?php if(count($ipd_info)>0){ ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
<strong>IPD Code :</strong><?=$ipd_info[0]->ipd_code?>
|
||||
<strong>/ Admit Date :</strong><?=$ipd_info[0]->str_register_date?>
|
||||
<strong>/ Dis. Date :</strong><?=$ipd_info[0]->str_discharge_date?>
|
||||
<strong>/ Charge Amt :</strong><?=$ipd_info[0]->charge_amount?>
|
||||
<strong>/ Medical Bill :</strong><?=$ipd_info[0]->med_amount?>
|
||||
<strong>IPD Code :</strong><?= esc($ipd_info[0]->ipd_code ?? '') ?>
|
||||
<strong>/ Admit Date :</strong><?= esc($ipd_info[0]->str_register_date ?? '') ?>
|
||||
<strong>/ Dis. Date :</strong><?= esc($ipd_info[0]->str_discharge_date ?? '') ?>
|
||||
<strong>/ Charge Amt :</strong><?= esc($ipd_info[0]->charge_amount ?? '') ?>
|
||||
<strong>/ Medical Bill :</strong><?= esc($ipd_info[0]->med_amount ?? '') ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label >Payment Bank Info</label>
|
||||
<input class="form-control varchar" id="input_pay_info" placeholder="Bank , NEFT Info" type="text" value="<?=$org_info[0]->amount_payment_info?>">
|
||||
<input class="form-control varchar" id="input_pay_info" placeholder="Bank , NEFT Info" type="text" value="<?= esc($org_info[0]->amount_payment_info ?? '') ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
|
|
|
|||
29
composer.lock
generated
29
composer.lock
generated
|
|
@ -8,16 +8,16 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "codeigniter4/framework",
|
||||
"version": "v4.7.0",
|
||||
"version": "v4.7.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/codeigniter4/framework.git",
|
||||
"reference": "e7753bc03f8b74af428f46b5e2bb74925487c930"
|
||||
"reference": "a1f129d56df3278702a8d1048af955d0ebf68c03"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/codeigniter4/framework/zipball/e7753bc03f8b74af428f46b5e2bb74925487c930",
|
||||
"reference": "e7753bc03f8b74af428f46b5e2bb74925487c930",
|
||||
"url": "https://api.github.com/repos/codeigniter4/framework/zipball/a1f129d56df3278702a8d1048af955d0ebf68c03",
|
||||
"reference": "a1f129d56df3278702a8d1048af955d0ebf68c03",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
"slack": "https://codeigniterchat.slack.com",
|
||||
"source": "https://github.com/codeigniter4/CodeIgniter4"
|
||||
},
|
||||
"time": "2026-02-01T20:39:35+00:00"
|
||||
"time": "2026-10-09T15:41:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "codeigniter4/settings",
|
||||
|
|
@ -726,30 +726,29 @@
|
|||
},
|
||||
{
|
||||
"name": "setasign/fpdi",
|
||||
"version": "v2.6.4",
|
||||
"version": "v2.6.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Setasign/FPDI.git",
|
||||
"reference": "4b53852fde2734ec6a07e458a085db627c60eada"
|
||||
"reference": "d97ade6368aa0b51b6316d5dc16a567e2bf60167"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada",
|
||||
"reference": "4b53852fde2734ec6a07e458a085db627c60eada",
|
||||
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/d97ade6368aa0b51b6316d5dc16a567e2bf60167",
|
||||
"reference": "d97ade6368aa0b51b6316d5dc16a567e2bf60167",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-zlib": "*",
|
||||
"php": "^7.1 || ^8.0"
|
||||
"php": ">=7.2 <=8.6.99999"
|
||||
},
|
||||
"conflict": {
|
||||
"setasign/tfpdf": "<1.31"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7",
|
||||
"setasign/fpdf": "~1.8.6",
|
||||
"phpunit/phpunit": "^8.5.52",
|
||||
"setasign/fpdf": "^1.9.0",
|
||||
"setasign/tfpdf": "~1.33",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"tecnickcom/tcpdf": "^6.8"
|
||||
},
|
||||
"suggest": {
|
||||
|
|
@ -786,7 +785,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Setasign/FPDI/issues",
|
||||
"source": "https://github.com/Setasign/FPDI/tree/v2.6.4"
|
||||
"source": "https://github.com/Setasign/FPDI/tree/v2.6.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -794,7 +793,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-05T09:57:14+00:00"
|
||||
"time": "2026-10-13T07:36:28+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
$projectRoot = dirname(__DIR__);
|
||||
$envPath = $projectRoot . DIRECTORY_SEPARATOR . '.env';
|
||||
|
||||
$envKey = '';
|
||||
if (is_readable($envPath)) {
|
||||
$lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if (is_array($lines)) {
|
||||
foreach ($lines as $line) {
|
||||
$line = trim((string) $line);
|
||||
if ($line === '' || strpos($line, '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripos($line, 'setup.dbToolsKey') !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = explode('=', $line, 2);
|
||||
$envKey = trim((string) ($parts[1] ?? ''), " \t\n\r\0\x0B\"'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$requestKey = trim((string) ($_GET['key'] ?? ''));
|
||||
if ($envKey === '') {
|
||||
http_response_code(403);
|
||||
echo "setup.dbToolsKey is not configured in .env\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($requestKey === '' || !hash_equals($envKey, $requestKey)) {
|
||||
http_response_code(403);
|
||||
echo "Invalid or missing key. Use: /setup_fs.php?key=YOUR_KEY\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$paths = [
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'cache',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'logs',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'session',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'uploads',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'debugbar',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'tmp',
|
||||
$projectRoot . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'uploads',
|
||||
];
|
||||
|
||||
$created = [];
|
||||
$chmodOk = [];
|
||||
$writableOk = [];
|
||||
$failed = [];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (!is_dir($path)) {
|
||||
if (@mkdir($path, 0775, true)) {
|
||||
$created[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir($path)) {
|
||||
if (@chmod($path, 0775)) {
|
||||
$chmodOk[] = $path;
|
||||
}
|
||||
|
||||
$probe = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '.write_test_' . str_replace('.', '', uniqid('', true)) . '.tmp';
|
||||
$written = @file_put_contents($probe, 'ok');
|
||||
if ($written !== false) {
|
||||
@unlink($probe);
|
||||
$writableOk[] = $path;
|
||||
} else {
|
||||
$failed[] = $path;
|
||||
}
|
||||
} else {
|
||||
$failed[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
echo "Filesystem preparation complete\n";
|
||||
echo "Project root: {$projectRoot}\n";
|
||||
echo "Created dirs: " . count($created) . "\n";
|
||||
echo "chmod attempted: " . count($chmodOk) . "\n";
|
||||
echo "Writable OK: " . count($writableOk) . "/" . count($paths) . "\n\n";
|
||||
|
||||
if (!empty($created)) {
|
||||
echo "Created:\n";
|
||||
foreach ($created as $item) {
|
||||
echo " - {$item}\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if (!empty($failed)) {
|
||||
echo "FAILED paths (need server shell fix):\n";
|
||||
foreach (array_values(array_unique($failed)) as $item) {
|
||||
echo " - {$item}\n";
|
||||
}
|
||||
|
||||
$writablePath = $projectRoot . '/writable';
|
||||
$uploadsPath = $projectRoot . '/public/uploads';
|
||||
|
||||
echo "\nRun these on server shell (example www-data):\n";
|
||||
echo "sudo chown -R www-data:www-data \"{$writablePath}\" \"{$uploadsPath}\"\n";
|
||||
echo "sudo find \"{$writablePath}\" -type d -exec chmod 775 {} \\;\n";
|
||||
echo "sudo find \"{$writablePath}\" -type f -exec chmod 664 {} \\;\n";
|
||||
echo "sudo chmod -R 775 \"{$uploadsPath}\"\n";
|
||||
} else {
|
||||
echo "All required paths are writable.\n";
|
||||
}
|
||||
|
||||
echo "\nNext:\n";
|
||||
echo "1) Open /setup/db-tools?key=... again\n";
|
||||
echo "2) Delete this file after success: public/setup_fs.php\n";
|
||||
27
public/uploads/.htaccess
Normal file
27
public/uploads/.htaccess
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Deny direct execution of scripts in uploads directory
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule \.(php|phtml|php3|php4|php5|php7|php8|phps|phar|cgi|pl|py|sh|asp|aspx|jsp|exe|bat|cmd)$ - [F,L,NC]
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|php8|phps|phar|cgi|pl|py|sh|asp|aspx|jsp|exe|bat|cmd)$">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order Deny,Allow
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</FilesMatch>
|
||||
|
||||
Options -Indexes -ExecCGI
|
||||
|
||||
<IfModule mod_php.c>
|
||||
php_flag engine off
|
||||
</IfModule>
|
||||
<IfModule mod_php7.c>
|
||||
php_flag engine off
|
||||
</IfModule>
|
||||
<IfModule mod_php8.c>
|
||||
php_flag engine off
|
||||
</IfModule>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
set timeout 300
|
||||
spawn scp -o StrictHostKeyChecking=no "d:\\Workplace\\HMS_CI4_OLD\\abdm-bridge-le-ssl.conf" root@159.89.175.190:/etc/apache2/sites-enabled/abdm-bridge-le-ssl.conf
|
||||
expect \"password:\"
|
||||
send \"bishT@9720958717H\r\"
|
||||
expect eof
|
||||
1
writable/setup_db_complete.lock
Normal file
1
writable/setup_db_complete.lock
Normal file
|
|
@ -0,0 +1 @@
|
|||
Setup locked on 2026-10-14 for WASA compliance.
|
||||
Loading…
Reference in New Issue
Block a user