MERGE_UAT_SECURITY_MPIN

This commit is contained in:
Ubuntu 2026-01-13 11:13:41 +05:30
commit fa8b38c4f4
22 changed files with 1449 additions and 212 deletions

View File

@ -183,3 +183,5 @@ CORS_MAX_AGE=7200
CORS_DEBUG=true
APP_SIGNATURE =
TOKENTIMEOUT =
JWT_SECRET =

View File

@ -5,6 +5,28 @@ Options -Indexes
# Rewrite engine
# ----------------------------------------------------------------------
## ADDED for - block any script execution inside folder of public
<If "%{REQUEST_URI} =~ m#/(logo|add_image_upload|e_card_imgs|claim_sample_forms|sample_import_excel|writable)/#">
Deny from all
# Disable PHP engine
<IfModule mod_php.c>
php_flag engine off
</IfModule>
# Disable CGI and other executable handlers
Options -ExecCGI
AddHandler cgi-script .php .pl .py .jsp .asp .sh .cgi
# Block access to any script-like files entirely
<FilesMatch "\.(php|php5|php7|phtml|pl|py|cgi|asp|aspx|sh|rb)$">
ForceType text/plain
#Order allow,deny
Deny from all
</FilesMatch>
</If>
# Turning on the rewrite engine is necessary for the following rules and features.
# FollowSymLinks must be enabled for this to work.
<IfModule mod_rewrite.c>

85
app/Config/Acl.php Normal file
View File

@ -0,0 +1,85 @@
<?php
namespace Config;
class Acl
{
public array $rules = [
// ===================== PUBLIC / AUTH =====================
'#^/login#' => ['public' => true],
'#^/logout#' => ['public' => true],
'#^/auth#' => ['public' => true],
'#^/oauth2callback#' => ['public' => true],
'#^/loginPos#' => ['public' => true],
'#^/getVerifyPosMobileNo#' => ['public' => true],
'#^/getVerifiedPosUserData#' => ['public' => true],
'#^/swagger#' => ['roles' => [ADMIN_ROLE_ID]],
'#^/fedeploy#' => ['roles' => [ADMIN_ROLE_ID]],
// ===================== PUBLIC DOWNLOADS / FORMS =====================
'#^/download-#' => ['public' => true],
'#^/claim-form-download#' => ['public' => true],
'#^/claims-feedback-form#' => ['public' => true],
'#^/autobookstackLogin#' => ['public' => true],
// ===================== DASHBOARD =====================
'#^/dashboard#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
'teams' => []
],
// ===================== USER MANAGEMENT =====================
'#^/user#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID],
'teams' => []
],
// ===================== CLIENT =====================
'#^/client#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
'teams' => []
],
// ===================== EMPLOYEE / ENROLLMENT =====================
'#^/employee#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
'teams' => [ENROLLMENT_TEAM_ID]
],
// ===================== MASTERS =====================
'#^/master#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
'teams' => []
],
'#^/util#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
'teams' => []
],
// ===================== LOGS =====================
'#^/logs#' => [
'roles' => [ADMIN_ROLE_ID],
'teams' => []
],
// ===================== INTERNAL TEST =====================
'#^/test#' => [
'roles' => [ADMIN_ROLE_ID],
'teams' => []
],
// ===================== API (JWT / SIGNED) =====================
'#^/api#' => ['public' => true],
'#^/employeeRest#' => ['public' => true],
'#^/clientApi#' => ['public' => true],
// ===================== CLI =====================
'#^/cli/#' => ['public' => true],
// ===================== DEFAULT DENY (ZERO TRUST) =====================
'#^/#' => [
'roles' => [ADMIN_ROLE_ID],
'teams' => []
],
];
}

View File

@ -16,6 +16,11 @@ use App\Filters\VerifyAppSignature;
use App\Filters\AuthJWT;
use App\Filters\Cors;
use App\Filters\GlobalPostFileUploadGuard;
use App\Filters\SecurityInputFilter;
use App\Filters\AclFilter;
class Filters extends BaseConfig
{
@ -39,6 +44,9 @@ class Filters extends BaseConfig
'CloseDbConnection' => CloseDbConnection::class,
'Cors' => Cors::class,
'appSignature' => VerifyAppSignature::class,
'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class,
'SecurityInputFilter' => SecurityInputFilter::class,
'AclFilter' => AclFilter::class,
];
@ -52,8 +60,10 @@ class Filters extends BaseConfig
public array $globals = [
'before' => [
'HttpRequestLog' => ['except' => 'cli/*'],
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','/employeeRest/*','processjob']],
'Cors',
// 'csrf',
'SecurityInputFilter' => ['except' => ['notification/create','test_mail'] ],
'GlobalPostFileUploadGuard',
// 'invalidchars',
],
'after' => [

View File

@ -453,9 +453,11 @@ $routes->group("/api", ["filter" => "authJWT"], function ($routes) {
// $routes->post("employeeRest/createOrUpdateEmployeePolicySiAmount", "EmployeeRestController::createOrUpdateEmployeePolicySiAmount");
// $routes->post("employeeRest/calculatePremium", "EmployeeRestController::calculatePremium");
// $routes->post("updateMpin", "RestAuthenticationController::updateMpin");
$routes->group("employeeRest", ['filter' => ['appSignature' , 'authJWT'] ], function ($routes) {
$routes->post('logout', 'RestAuthenticationController::logout');
// $routes->post("getVerifiedUserData", "RestAuthenticationController::getVerifiedUserData");
$routes->post("storeFireBase", "EmployeeRestController::storeFireBase");

View File

@ -179,7 +179,7 @@ class EmployeeController extends AdminController
}
//handles employee & dependent bulk upload with events like inception,addition,deletion, correction and SI enhancements
public function employeesUplodWithEvents()
public function employeesUplodWithEvents($post_data = [])
{
// $empDataServiceController = new EmpDataServiceController();
@ -233,82 +233,184 @@ class EmployeeController extends AdminController
// $this->truncateFileData(747, 5) ;
// print_rr($this->cloneWorksheet());
// die();
if ($this->request->getMethod() == 'post') {
// old
// if ($this->request->getMethod() == 'post') {
// //validate uploaded file
// $filename = '';
// $fileSize = '';
// $validated = $this->validate([
// 'emplist' => [
// 'uploaded[emplist]',
// 'mime_in[emplist,application/vnd.ms-excel,application/vnd,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
// 'max_size[emplist,16384]',
// ],
// ]);
// if ($validated)
// {
// $avatar = $this->request->getFile('emplist');
// if (!$avatar) {
// $this->myLogger->logme("error", 'File not found');
// return $this->respond(['dataStatus' => false, 'code' => 400, 'message' => 'File not found'], 400);
// }
// $is_moved = $avatar->move(WRITEPATH . 'uploads/excel/');
// if ($is_moved) {
// $filename = $avatar->getName();
// $fileSize = $avatar->getSize(); // File size in bytes
// $fileSize = $fileSize / (1024 * 1024); // Convert to MB
// // Handle successful upload, e.g., log success or further processing
// $this->myLogger->logme("error", 'File move successful');
// } else {
// $this->myLogger->logme("error", 'File move failed');
// return $this->respond(['dataStatus' => false, 'code' => 500, 'message' => 'File move failed'], 500);
// }
// } else {
// $this->myLogger->logme("error", 'Upload failed Invalid file');
// return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
// }
// //process post variable entry in file table
// $loggedInUserID = get_session_userid();
// // dd($loggedInUserID);
// // $loggedInUserID = 8;
// $client_id = $this->request->getPost('client_id');
// $policy_id = $this->request->getPost('policy_id');
// $branch_id = $this->request->getPost('branch_id');
// $enrollment_open_date = $this->request->getPost('enrollment_open_date');
// $enrollment_close_date = $this->request->getPost('enrollment_close_date');
// $action = $this->request->getPost('upload-action-type');
// $status = 'inprogress';
// $enrollment_open_date = change_date_format($enrollment_open_date, 'd/m/Y', 'Y-m-d');
// $enrollment_close_date = change_date_format($enrollment_close_date, 'd/m/Y', 'Y-m-d');
// $file_id = $this->fileModel->insert(['file_name' => $filename, 'client_id' => $client_id, 'policy_id' => $policy_id, 'created_by' => $loggedInUserID, 'status' => $status, 'action' => $action, 'client_branch_id' => $branch_id,'uploaded_by' => 1, 'enrollment_open_date' => $enrollment_open_date, 'enrollment_close_date' => $enrollment_close_date]); //here field policy_id have client_policy_id and not policy id from policy master
// $this->myLogger->logme("error", '{file_id} uploaded success', ['file_id' => $file_id]);
// //start validation process
// if($fileSize < 1) // if file size less than 1
// {
// $empServiceController = new EmployeeServiceController();
// $result = $empServiceController->excelFileFormatValidation(['file_id' => $file_id]);
// $this->myLogger->logme("error", '{file_id} is less than 1MB, validating on the fly', ['file_id' => $file_id]);
// //endof validation process
// if (isset($result['error_summary']) && count($result['error_summary'])) {
// return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'file rejected with errors'], 200);
// }
// }
// else //if file size greater than 1 add the file as job
// {
// $job_details = new Jobs();
// $r = Jobs::addJob(['job_name' => 'excelFileFormatValidation','payload' => ['file_id' => $file_id]]);
// $this->myLogger->logme("error", '{file_id} is greather than 1MB, validating with job queue', ['file_id' => $file_id]);
// }
// return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => 'file upload success'], 200);
// }
if (!empty($post_data) || $this->request->is('post') == 'post') {
//validate uploaded file
$filename = '';
$fileSize = '';
$validated = $this->validate([
'emplist' => [
'uploaded[emplist]',
'mime_in[emplist,application/vnd.ms-excel,application/vnd,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
'max_size[emplist,16384]',
],
]);
if ($validated)
{
$avatar = $this->request->getFile('emplist');
if (!$avatar) {
$this->myLogger->logme("error", 'File not found');
if (empty($post_data)) {
$validated = $this->validate([
'emplist' => [
'uploaded[emplist]',
'mime_in[emplist,application/vnd.ms-excel,application/vnd,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet]',
'max_size[emplist,16384]',
],
]);
} else {
$validated = validateExcelFile($post_data['emplist']);
}
if ($validated) {
$avatar = isset($post_data['emplist']) ? $post_data['emplist'] : $this->request->getFile('emplist');
if (!$avatar) {
$this->myLogger->logme("error", 'File not found');
if (!empty($post_data)) {
return ['status' => false, 'message' => 'File not found'];
} else {
return $this->respond(['dataStatus' => false, 'code' => 400, 'message' => 'File not found'], 400);
}
}
$is_moved = $avatar->move(WRITEPATH . 'uploads/excel/');
if ($is_moved) {
$filename = $avatar->getName();
$fileSize = $avatar->getSize(); // File size in bytes
$fileSize = $fileSize / (1024 * 1024); // Convert to MB
// Handle successful upload, e.g., log success or further processing
$this->myLogger->logme("error", 'File move successful');
$is_moved = $avatar->move(WRITEPATH . 'uploads/excel/');
if ($is_moved) {
$filename = $avatar->getName();
$fileSize = $avatar->getSize(); // File size in bytes
$fileSize = $fileSize / (1024 * 1024); // Convert to MB
// Handle successful upload, e.g., log success or further processing
$this->myLogger->logme("error", 'File move successful');
} else {
$this->myLogger->logme("error", 'File move failed');
if (!empty($post_data)) {
return ['status' => false, 'message' => 'File move failed'];
} else {
$this->myLogger->logme("error", 'File move failed');
return $this->respond(['dataStatus' => false, 'code' => 500, 'message' => 'File move failed'], 500);
}
}
} else {
$this->myLogger->logme("error", 'Upload failed Invalid file');
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
if (!empty($post_data)) {
return ['status' => false, 'message' => 'Invalid file'];
} else {
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'Invalid file'], 404);
}
}
//process post variable entry in file table
$loggedInUserID = get_session_userid();
// dd($loggedInUserID);
// $loggedInUserID = 8;
$loggedInUserID = $post_data['created_by'] ?? get_session_userid();
$client_id = $this->request->getPost('client_id');
$policy_id = $this->request->getPost('policy_id');
$branch_id = $this->request->getPost('branch_id');
$enrollment_open_date = $this->request->getPost('enrollment_open_date');
$enrollment_close_date = $this->request->getPost('enrollment_close_date');
$action = $this->request->getPost('upload-action-type');
$status = 'inprogress';
$client_id = isset($post_data['client_id']) ? $post_data['client_id'] : $this->request->getPost('client_id');
$policy_id = isset($post_data['policy_id']) ? $post_data['policy_id'] : $this->request->getPost('policy_id');
$branch_id = isset($post_data['client_branch_id']) ? $post_data['client_branch_id'] : $this->request->getPost('branch_id');
$action = isset($post_data['file_action']) ? $post_data['file_action'] : $this->request->getPost('upload-action-type');
$enrollment_open_date = isset($post_data['enrollment_open_date']) ? $post_data['enrollment_open_date'] : $this->request->getPost('enrollment_open_date') ?? null;
$enrollment_close_date = isset($post_data['enrollment_close_date']) ? $post_data['enrollment_close_date'] : $this->request->getPost('enrollment_close_date') ?? null;
$status = 'inprogress';
$hr_id = $post_data['created_by'] ?? null;
$enrollment_open_date = change_date_format($enrollment_open_date, 'd/m/Y', 'Y-m-d');
$enrollment_close_date = change_date_format($enrollment_close_date, 'd/m/Y', 'Y-m-d');
$file_id = $this->fileModel->insert(['file_name' => $filename, 'client_id' => $client_id, 'policy_id' => $policy_id, 'created_by' => $loggedInUserID, 'status' => $status, 'action' => $action, 'client_branch_id' => $branch_id,'uploaded_by' => 1, 'enrollment_open_date' => $enrollment_open_date, 'enrollment_close_date' => $enrollment_close_date]); //here field policy_id have client_policy_id and not policy id from policy master
$file_id = $this->fileModel->insert(['file_name' => $filename, 'client_id' => $client_id, 'policy_id' => $policy_id, 'created_by' => $loggedInUserID, 'status' => $status, 'action' => $action, 'client_branch_id' => $branch_id, 'uploaded_by' => 1, 'enrollment_open_date' => $enrollment_open_date, 'enrollment_close_date' => $enrollment_close_date, 'hr_id' => $hr_id]); //here field policy_id have client_policy_id and not policy id from policy master
$this->myLogger->logme("error", '{file_id} uploaded success', ['file_id' => $file_id]);
//start validation process
if($fileSize < 1) // if file size less than 1
if ($fileSize < 1) // if file size less than 1
{
$empServiceController = new EmployeeServiceController();
$result = $empServiceController->excelFileFormatValidation(['file_id' => $file_id]);
$this->myLogger->logme("error", '{file_id} is less than 1MB, validating on the fly', ['file_id' => $file_id]);
//endof validation process
if (isset($result['error_summary']) && count($result['error_summary'])) {
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'file rejected with errors'], 200);
if(!empty($post_data)){
return ['status' => true, 'message' => 'file rejected with errors', 'file_id' => $file_id];
}else{
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'file rejected with errors'], 200);
}
}
}
else //if file size greater than 1 add the file as job
} else //if file size greater than 1 add the file as job
{
$job_details = new Jobs();
$r = Jobs::addJob(['job_name' => 'excelFileFormatValidation','payload' => ['file_id' => $file_id]]);
$r = Jobs::addJob(['job_name' => 'excelFileFormatValidation', 'payload' => ['file_id' => $file_id]]);
$this->myLogger->logme("error", '{file_id} is greather than 1MB, validating with job queue', ['file_id' => $file_id]);
}
return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => 'file upload success'], 200);
if (!empty($post_data)) {
return ['status' => true, 'message' => 'File upload successs, Data validation is in-progress', 'file_id' => $file_id];
} else {
return $this->respond(['dataStatus' => true, 'code' => 200, 'data' => 'file upload success'], 200);
}
}
$data['page_name'] = 'View Inception';
@ -335,7 +437,7 @@ class EmployeeController extends AdminController
->select([
'files.id','files.file_name','files.created_by','files.created_at','files.is_active','files.status','files.client_id','files.policy_id','files.client_branch_id','files.action','files.uploaded_by', 'enrollment_open_date', 'enrollment_close_date',
'up.emp_code',
'up.first_name',
// 'up.first_name',
'c.short_name',
'cb.branch_name',
'cp.id as client_policy_id',
@ -349,8 +451,23 @@ class EmployeeController extends AdminController
WHERE e.file_id = files.id AND ep.client_policy_id = files.policy_id) as total',
'policy_type.policy_type' ,
'cp.policy_no' ,
"CASE
WHEN files.hr_id IS NOT NULL THEN
CONCAT(
(
SELECT lc.name
FROM level_contacts lc
WHERE lc.id = files.hr_id
LIMIT 1
),
' (HR)'
)
ELSE up.first_name
END AS first_name
",
'files.hr_id'
])
->join('user_profiles up', 'files.created_by = up.id')
->join('user_profiles up', 'files.created_by = up.id', 'left')
->join('client_policy cp', 'files.policy_id = cp.id', 'left')
->join('client_branch cb', 'files.client_branch_id = cb.id', 'left')
->join('policy_type', 'policy_type.id = cp.policy_type_id')
@ -383,14 +500,22 @@ class EmployeeController extends AdminController
}
}
public function getExcelFileErrors()
public function getExcelFileErrors($file_id, $retun_type)
{
$file_id = $this->request->uri->getSegment(3);
// $file_id = $this->request->uri->getSegment(3);
$empServiceController = new EmployeeServiceController();
// Render views and capture output
$result = $empServiceController->getExcelErrorData($file_id);
if($retun_type == 'api'){
if(!empty($result)){
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Error data feteched successfully', 'data' => $result], 200);
}else{
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to fetch error data', 'data' => []], 200);
}
}
if ($result != 0) {
$result['file_id'] = $file_id;
@ -2833,7 +2958,7 @@ class EmployeeController extends AdminController
if (!is_dir($exportDir)) {
mkdir($exportDir, 0777, true);
} else {
chmod($exportDir, 0777);
// chmod($exportDir, 0777);
}
$filename = 'inception_export_' . date('Ymd_His') . '.xlsx';

View File

@ -786,8 +786,48 @@ class EmployeeRestController extends AdminController
}
// Upload the Employee Detail in DB by Sheet Data
public function employeeUpload()
{
try{
$post_data = [
'client_id' => $this->request->getPost('client_id') ?? null,
'client_branch_id' => $this->request->getPost('client_branch_id'),
'policy_id' => $this->request->getPost('policy_id'),
'enrollment_open_date' => $this->request->getPost('enrollment_open_date'),
'enrollment_close_date' => $this->request->getPost('enrollment_close_date'),
'file_action' => "enrollment",
'created_by' => $this->request->getPost('created_by'),
'emplist' => $this->request->getFile('file')
];
// print_r($post_data); die;
if(empty($post_data['client_id'])){
return $this->respondCreated(['status' => false, 'message' => 'Client is required', 'data' => []]);
}
$employeeController = new EmployeeController();
$responce = $employeeController->employeesUplodWithEvents($post_data);
// print_r($responce); die;
if($responce['status']){
$file_data = $this->getDataFromFilesTable(['file_id' => $responce['file_id']]);
$responce['data'] = $file_data;
return $this->respond($responce, 200);
}else{
$responce['data'] = [];
return $this->respond($responce, 200);
}
}catch(\Exception $e){
return $this->respondCreated(['status' => false, 'message' => $e->getMessage(), 'data' => []]);
}
}
// Upload the Employee Detail in DB by Sheet Data (DO NOT REMOVE THIS)
public function employeeUploadOld()
{
try {
$file = $this->request->getFile('file');
@ -1145,9 +1185,69 @@ class EmployeeRestController extends AdminController
return $this->respond(['status' => 'failed','code' => 500,'data' => $th->getMessage(), 'error_data' => $errorData], 500);
}
}
public function getDataFromFilesTable($search_data)
{
$file_download_base = base_url('util/download-file-list/');
$builder = $this->fileModel
->select("
files.id,
files.client_id,
files.client_branch_id,
files.policy_id,
cp.policy_no,
files.file_name,
files.action,
files.created_at,
files.created_by,
files.updated_at,
files.updated_by,
c.short_name,
cb.branch_name,
lc.name as first_name,
CONCAT(UCASE(LEFT(files.status, 1)), LCASE(SUBSTRING(files.status, 2))) as status,
CASE
WHEN status = 'failed' THEN 1
ELSE 0
END AS file_error_status,
CONCAT('{$file_download_base}', files.id, '/api') AS file_download_link
", false)
->join('clients c', 'files.client_id = c.id AND c.is_active = 1', 'left')
->join('client_branch cb', 'files.client_branch_id = cb.id AND cb.is_active = 1', 'left')
->join('client_policy cp', 'files.policy_id = cp.id AND cp.is_active = 1', 'left')
->join('level_contacts lc', 'files.hr_id = lc.id AND lc.contact_type = "client" AND lc.is_active = 1', 'left');
if (isset($search_data['policy_id']) && !empty($search_data['policy_id'])) {
$builder->where("files.policy_id", $search_data['policy_id']);
}
if (isset($search_data['created_by']) && !empty($search_data['created_by'])) {
$builder->where("files.hr_id", $search_data['created_by']);
}
if (isset($search_data['policy_no']) && !empty($search_data['policy_no'])) {
$builder->where("cp.policy_no", $search_data['policy_no']);
}
if (isset($search_data['client_id']) && !empty($search_data['client_id'])) {
$builder->where("files.client_id", $search_data['client_id']);
}
if (isset($search_data['file_id']) && !empty($search_data['file_id'])) {
$builder->where("files.id", $search_data['file_id']);
}
// Execute query
$data = $builder->get()->getResultArray();
if (!empty($data)) {
return $data;
}
return [];
}
@ -1463,13 +1563,13 @@ class EmployeeRestController extends AdminController
$array->mapped_family_floaters = $data;
$array->type = "GMC";
$array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0;
$array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0.0;
if($array->is_premium_summery == 1){
$array->family_floaters_of_dependent_and_si_premium_value = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0;
$array->family_floaters_of_dependent_and_gst_value = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0;
$array->family_floaters_of_dependent_and_si_premium_value = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0.0;
$array->family_floaters_of_dependent_and_gst_value = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0.0;
}else{
$array->family_floaters_of_dependent_and_si_premium_value = 0;
$array->family_floaters_of_dependent_and_gst_value = 0;
$array->family_floaters_of_dependent_and_si_premium_value = 0.0;
$array->family_floaters_of_dependent_and_gst_value = 0.0;
}
$checkGmcParentsPolicyExist = $this->clientPolicyModel->select("
@ -1531,7 +1631,8 @@ class EmployeeRestController extends AdminController
}
if(!empty($result) && $this->request->getGet('policy') == 'GMC'){
return $this->respond(['status' => 'success','code' => 200,'data' => $result], 200);
// return $this->respond(['status' => 'success','code' => 200,'data' => $result], 200);
echo json_encode(['status' => 'success','code' => 200,'data' => $result], JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION);
}else{
return $this->respond(['status' => 'success','code' => 200,'data' => []], 200);
}
@ -1775,13 +1876,13 @@ class EmployeeRestController extends AdminController
$array->mapped_family_floaters = $data;
$array->type = "GMC - Parents";
$array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0;
$array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0.0;
if($array->is_premium_summery == 1){
$array->family_floaters_of_dependent_and_si_premium_value = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0;
$array->family_floaters_of_dependent_and_gst_value = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0;
$array->family_floaters_of_dependent_and_si_premium_value = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0.0;
$array->family_floaters_of_dependent_and_gst_value = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0.0;
}else{
$array->family_floaters_of_dependent_and_si_premium_value = 0;
$array->family_floaters_of_dependent_and_gst_value = 0;
$array->family_floaters_of_dependent_and_si_premium_value = 0.0;
$array->family_floaters_of_dependent_and_gst_value = 0.0;
}
return $array;
@ -2424,13 +2525,13 @@ class EmployeeRestController extends AdminController
}
$responce['family_floaters_of_dependent_and_si_array'] = $data;
$responce['family_floaters_of_dependent_and_si_value'] = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0;
$responce['family_floaters_of_dependent_and_si_value'] = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0.0;
if($array['is_premium_summery']){
$responce['family_floaters_of_dependent_and_si_premium_value'] = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0;
$responce['family_floaters_of_dependent_and_gst_value'] = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0;
$responce['family_floaters_of_dependent_and_si_premium_value'] = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0.0;
$responce['family_floaters_of_dependent_and_gst_value'] = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0.0;
}else{
$responce['family_floaters_of_dependent_and_si_premium_value'] = 0;
$responce['family_floaters_of_dependent_and_gst_value'] = 0;
$responce['family_floaters_of_dependent_and_si_premium_value'] = 0.0;
$responce['family_floaters_of_dependent_and_gst_value'] = 0.0;
}
@ -2439,7 +2540,8 @@ class EmployeeRestController extends AdminController
if($this->request->getGet('policy') == 'GMC-DEPENDENT-ADDON'){
return $this->respond(['status' => 'success','code' => 200,'data' => ['gmc_dependent_addon'=>$responce]], 200);
// return $this->respond(['status' => 'success','code' => 200,'data' => ['gmc_dependent_addon'=>$responce]], 200);
echo json_encode(['status' => 'success','code' => 200,'data' => ['gmc_dependent_addon'=>$responce]], JSON_PRETTY_PRINT | JSON_PRESERVE_ZERO_FRACTION);
}
//array_push($PolicyData, $responce);
@ -2606,7 +2708,8 @@ class EmployeeRestController extends AdminController
$array_list = [];
foreach ($client_policy_id as $key => $value)
{
$policy = $this->clientPolicyModel->where('id',$value)->where('open_for_enrollment',1)->find();
// $policy = $this->clientPolicyModel->where('id',$value)->where('open_for_enrollment',1)->find();
$policy = $this->findThePolicyIsOpenForEnrollment($client_policy_id, $emp_code);
if($policy)
{
$this->myLogger->logme("error", 'client policy id = '.$value.' is open for enrollment');

View File

@ -9,6 +9,7 @@ use Psr\Log\LoggerInterface;
use App\Models\UserModel;
use App\Models\AuthHistoryModel;
use App\Libraries\AuthLogout;
class LoginController extends BaseController
{
@ -30,20 +31,22 @@ class LoginController extends BaseController
public function receiveGoogleOAuthResponse()
{
$UserModel = new UserModel();
//echo 'DONE';die();
// echo 'DONE';die();
if ($this->request->getGet('code')) {
$code = (string) $this->request->getGet('code');
log_message('error', 'Get OAuth Responce Code Sucessfully');
log_message('error', 'OAuthResponceCode : `'.$code.'`');
$value = googleOAuthLogin($this->request->getGet('code'));
// log_message('error', "OAuthResponceCode :" . json_encode($value));
if($value){
// print_r($value);//die;
$user = $UserModel->getUserByEmail($value->email);
if($user){
if($user->is_active !== '0'){
$user_team = $UserModel->getUserTeamsByUserID($user->id);
// dd($user_team);
session()->regenerate(true);
$session_data = [
'isLoggedIn' => True ,
'userid' => $user->id,
@ -54,25 +57,29 @@ class LoginController extends BaseController
$path = getenv('cookie.Path');
$domain = getenv('cookie.Domain');
$https = getenv('ccokie.secure');
setcookie('session_data', json_encode($session_data), time() + 12 * 60 * 60, $path, $domain, $https, true);
// setcookie('session_data', json_encode($session_data), time() + 12 * 60 * 60, $path, $domain, $https, true);
set_session_data($session_data);
// Bind session to device
set_session_data(['fingerprint' => hash('sha256',
($this->request->getUserAgent()->getAgentString() . '|' . ($this->request->getIPAddress()
)))]);
log_message('error', 'Set The UserId : `'. $user->id .'` in Session');
log_message('error', 'User Login Sucessfully');
$this->getUserDeviceInfo($user->id, 'NhanceUser');
return redirect()->to(base_url('/dashboard/view'));
return redirect()->to(base_url('/dashboard/view'));
}else{
log_message('error', 'User Not Active');
session()->setFlashdata('error', 'User Not Active');
return redirect()->to(base_url('login'));
return AuthLogout::logout();
}
}else{
log_message('error', 'User Not Registered');
session()->setFlashdata('error', 'User Not Registered');
return redirect()->to(base_url('login'));
return AuthLogout::logout();
}
}
@ -94,14 +101,16 @@ class LoginController extends BaseController
// setcookie('session_data', '', time() - 3600, $path);
// return redirect()->to(base_url('login'));
$path = getenv('cookie.Path');
session()->destroy();
// setcookie('session_data', '', time() - 3600, $path);
$path = getenv('cookie.Path');
$domain = getenv('cookie.Domain');
$https = getenv('cookie.secure');
setcookie('session_data',null, time() -3600, $path, $domain, $https, true);
return redirect()->to(base_url('login'));
// $path = getenv('cookie.Path');
// session()->destroy();
// // setcookie('session_data', '', time() - 3600, $path);
// $path = getenv('cookie.Path');
// $domain = getenv('cookie.Domain');
// $https = getenv('cookie.secure');
// setcookie('session_data',null, time() -3600, $path, $domain, $https, true);
// return redirect()->to(base_url('login'));
return AuthLogout::logout();
}

View File

@ -1742,6 +1742,7 @@ class MasterController extends AdminController
'cache' => WRITEPATH . 'cache',
'sample_import_excel' => ROOTPATH . 'public/sample_import_excel',
'lead_files' => WRITEPATH . 'uploads/lead_files/',
'exports' => WRITEPATH . 'exports/',
];
foreach ($folders as $folderName => $folderPath) {

View File

@ -166,7 +166,14 @@ class RestAuthenticationController extends AdminController
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithMobileNumber: Final employee data to verify = " . json_encode($employeeData));
if (isset($employeeData['employee_id']))
{
{
//check the resend otp (with in 60 seconds don't allow another otp to send)
$check = canSendOtp($employeeData);
if (!$check['allowed']) {
return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests
}
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithMobileNumber: Employee verified with ID = " . $employeeData['employee_id']);
log_message('error', ' ');
log_message('error', '************************ PRE END ********************************');
@ -264,10 +271,11 @@ class RestAuthenticationController extends AdminController
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Received Email ID = " . $email);
$empdata = RestAuthHelper::getPreAndPostDataByEmailOrMobile(['email_id' => $email]);
// print_r($empdata); die;
$otp = random_int(100000, 999999);
//only retail policy
if(empty($empdata)){
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: No employee data found both PRE & POST");
log_message('error', ' ');
@ -323,8 +331,15 @@ class RestAuthenticationController extends AdminController
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Using PRE data");
}
if (isset($employeeData['employee_id']))
{
//check the resend otp (with in 60 seconds don't allow another otp to send)
$check = canSendOtp($employeeData);
if (!$check['allowed']) {
return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests
}
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithEmailId: Valid employee found, generating OTP");
@ -578,6 +593,12 @@ class RestAuthenticationController extends AdminController
if ($HrData)
{
//check the resend otp (with in 60 seconds don't allow another otp to send)
$check = canSendOtp($HrData);
if (!$check['allowed']) {
return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests
}
$sql = "UPDATE level_contacts SET otp = ? WHERE mobile = ? AND contact_type = 'client' AND is_active = 1";
@ -637,6 +658,12 @@ class RestAuthenticationController extends AdminController
$data->otp = $otp;
if ($HrData) {
//check the resend otp (with in 60 seconds don't allow another otp to send)
$check = canSendOtp($HrData);
if (!$check['allowed']) {
return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests
}
@ -951,6 +978,12 @@ class RestAuthenticationController extends AdminController
$old_mpin = $this->request->getJSON()->old_mpin;
$mpin = $this->request->getJSON()->new_mpin;
if($old_mpin == $mpin)
{
$result = ['mpin_verification' => false , 'message' => "New MPIN must be different from the old MPIN"];
return $this->respond(['status' => 'failed','code' => 404,'data' => $result],200);
}
$empdata = RestAuthHelper::getPreAndPostDataByEmailOrMobile(['email_id' => $email_id, 'mobile_number' => $mobile_number, 'old_mpin' => $old_mpin ]);
// print_r($empdata); die;
@ -959,7 +992,7 @@ class RestAuthenticationController extends AdminController
log_message('error', ' ');
log_message('error', '************************ PRE END ********************************');
$result = ['user_verification' => false , 'message' => "User not found"];
$result = ['mpin_verification' => false , 'message' => "Old MPIN is incorrect"];
return $this->respond(['status' => 'failed','code' => 404,'data' => $result],200);
}
@ -1238,7 +1271,7 @@ class RestAuthenticationController extends AdminController
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - Exist");
log_message('error', ' ');
log_message('error', '************************ PRE END ********************************');
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'], 'is_biometric_enabled' => $employeeData['is_biometric_enabled']],200);
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? 0, 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? 0 ],200);
} else {
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - not found in PRE so call the thirdpartapi to the POST to check the MPIN");
log_message('error', ' ');
@ -1868,5 +1901,55 @@ class RestAuthenticationController extends AdminController
}
}
public function logout()
{
$authHeader = $this->request->getHeaderLine('Authorization');
if (!$authHeader) {
return $this->respond([
'status' => false,
'message' => 'Authorization token missing'
], 401);
}
// Validate JWT (your hardened function)
$result = JWTToken::validateJWT($authHeader);
if ($result['status'] !== true) {
return $this->respond([
'status' => false,
'message' => 'Invalid or expired token'
], 401);
}
$decoded = $result['decoded'];
$userId = $decoded['id'] ?? null;
if (!$userId) {
return $this->respond([
'status' => false,
'message' => 'Invalid token payload'
], 401);
}
// Identify user type
if (isset($decoded['emp_code'])) {
$model = new EmployeeModel();
} else {
$model = new LevelContactModel();
}
// Invalidate token server-side
$model->update($userId, [
'token_time_out' => null
]);
return $this->respond([
'status' => true,
'message' => 'Logged out successfully'
], 200);
}
}

156
app/Filters/AclFilter.php Normal file
View File

@ -0,0 +1,156 @@
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
use Config\Acl;
use App\Libraries\AuthLogout;
class AclFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
// ===================== CLI BYPASS =====================
if (is_cli()) {
return;
}
// ===================== PATH NORMALIZATION =====================
$uri = service('uri');
// Raw path: /PHP828APPS/ruc/nhance/index.php/dashboard/view
$fullPath = '/' . ltrim($uri->getPath(), '/');
// Base path: /PHP828APPS/ruc/nhance
$basePath = rtrim(parse_url(base_url(), PHP_URL_PATH), '/');
// Remove base path
if ($basePath && str_starts_with($fullPath, $basePath)) {
$path = substr($fullPath, strlen($basePath));
} else {
$path = $fullPath;
}
// Remove index.php if present
if (str_starts_with($path, '/index.php')) {
$path = substr($path, strlen('/index.php'));
}
// Normalize
$path = '/' . ltrim($path, '/');
// Fallback
if ($path === '') {
$path = '/';
}
// echo'<br>BASH PATH: ' . base_url();
// echo'<br>ACL RAW PATH: ' . $fullPath;
// echo'<br>ACL BASE PATH: ' . $basePath;
// echo'<br>ACL FINAL PATH: ' . $path;
// ===================== LOAD ACL =====================
$acl = new Acl();
$rules = $acl->rules;
// print_rr($rules);die;
// ===================== MATCH RULE =====================
$matchedRule = null;
foreach ($rules as $pattern => $rule) {
// echo "$pattern".'---------<br>';
if (preg_match($pattern, $path)) {
// echo "matched - $pattern";
$matchedRule = $rule;
break; // FIRST MATCH WINS
}
}
// print_r($matchedRule);//die;
// ===================== NO RULE = DENY =====================
if ($matchedRule === null) {
return $this->deny($path, 'No ACL rule matched');
}
// ===================== PUBLIC ROUTE =====================
if (!empty($matchedRule['public'])) {
return; // ALLOW
}
// ===================== AUTH CHECK =====================
if (!check_session()) {
// For API requests return 401 JSON
if ($request->isAJAX() || str_starts_with($path, '/api') || str_starts_with($path, '/employeeRest')) {
return service('response')
->setStatusCode(401)
->setJSON(['error' => 'Unauthorized']);
}
// For web redirect to login
return AuthLogout::logout();
}
// ===================== GET USER CONTEXT =====================
$userRole = check_role(); //
$userTeams = user_team(); // must return array of TEAM IDs
$allowedRoles = $matchedRule['roles'] ?? [];
$allowedTeams = $matchedRule['teams'] ?? [];
// ===================== ROLE FIRST =====================
if (!empty($allowedRoles) && in_array((int)$userRole, $allowedRoles, true)) {
return; // ALLOW
}
// ===================== TEAM FALLBACK =====================
if (!empty($allowedTeams) && is_array($userTeams)) {
foreach ($userTeams as $teamId) {
if (in_array($teamId, $allowedTeams, true)) {
return; // ALLOW
}
}
}
// ===================== DENY =====================
return $this->deny($path, 'Role/Team not permitted');
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// nothing
}
// ===================== DENY HANDLER =====================
protected function deny(string $path, string $reason)
{
log_message('error', 'ACL BLOCKED: {user} {path} - {reason}', [
'user' => session()->get('userid') ?? 'guest',
'path' => $path,
'reason' => $reason,
]);
// API / AJAX → JSON
$request = service('request');
if ($request->isAJAX() || str_starts_with($path, '/api') || str_starts_with($path, '/employeeRest')) {
return service('response')
->setStatusCode(403)
->setJSON([
'error' => 'Forbidden',
'message' => 'You do not have permission to access this resource'
]);
}
$response = service('response');
$response->setStatusCode(403);
$response->setBody(view('errors/404', [
'message' => '403 Access denied - You do not have permission to access this resource'
]));
return $response;
// Web → nice 403 page or simple text
return service('response')
->setStatusCode(403)
->setBody('403 Forbidden - Access denied - You do not have permission to access this resource');
}
}

View File

@ -18,65 +18,116 @@ use App\Models\LevelContactModel;
class AuthJWT implements FilterInterface
{
// public function before(RequestInterface $request, $arguments = null)
// {
// $jwt = $request->getHeaderLine('Authorization');
// if ($jwt) {
// if (JWTToken::validateJWT($jwt)) {
// $data = JWTToken::validateJWT($jwt);
// $data = json_decode($data);
// $id = $data->decoded->id;
// if(isset($data->decoded->emp_code)){
// $model = new EmployeeModel();
// $user_data = $model->where('id', $id)->first();
// if($user_data['token_time_out'] > time()){
// $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ];
// $model->update($id, $data);
// return true;
// }else{
// // if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL)
// $data =["token_time_out" => ''];
// $model->update($id, $data);
// header('Content-Type: application/json');
// http_response_code(401);
// // $error = json_encode(["status" => 401, "message" => $data->message]);
// $error = json_encode(["status" => 401, "message" => "Token is Invalid"]);
// echo $error;
// exit;
// }
// }else{
// $model = new LevelContactModel();
// $hr_data = $model->where('id', $id)->first();
// if($hr_data['token_time_out'] > time()){
// $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ];
// $model->update($id, $data);
// return true;
// }else{
// $data =["token_time_out" => ''];
// $model->update($id, $data);
// header('Content-Type: application/json');
// http_response_code(401);
// // $error = json_encode(["status" => 401, "message" => $data->message]);
// $error = json_encode(["status" => 401, "message" => "Token is Invalid"]);
// echo $error;
// exit;
// }
// }
// }
// } else {
// header('Content-Type: application/json');
// http_response_code(403);
// $error = json_encode(["status" => 403, "message" => "Access Forbidden!"]);
// echo $error;
// exit();
// }
// }
public function before(RequestInterface $request, $arguments = null)
{
$jwt = $request->getHeaderLine('Authorization');
$authHeader = $request->getHeaderLine('Authorization');
if ($jwt) {
if (JWTToken::validateJWT($jwt)) {
$data = JWTToken::validateJWT($jwt);
$data = json_decode($data);
$id = $data->decoded->id;
if(isset($data->decoded->emp_code)){
$model = new EmployeeModel();
$user_data = $model->where('id', $id)->first();
if($user_data['token_time_out'] > time()){
$data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ];
$model->update($id, $data);
return true;
}else{
// if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL)
$data =["token_time_out" => ''];
$model->update($id, $data);
header('Content-Type: application/json');
http_response_code(401);
// $error = json_encode(["status" => 401, "message" => $data->message]);
$error = json_encode(["status" => 401, "message" => "Token is Invalid"]);
echo $error;
exit;
}
}else{
$model = new LevelContactModel();
$hr_data = $model->where('id', $id)->first();
if($hr_data['token_time_out'] > time()){
$data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ];
$model->update($id, $data);
return true;
}else{
$data =["token_time_out" => ''];
$model->update($id, $data);
header('Content-Type: application/json');
http_response_code(401);
// $error = json_encode(["status" => 401, "message" => $data->message]);
$error = json_encode(["status" => 401, "message" => "Token is Invalid"]);
echo $error;
exit;
}
}
}
} else {
header('Content-Type: application/json');
http_response_code(403);
$error = json_encode(["status" => 403, "message" => "Access Forbidden!"]);
echo $error;
exit();
if (!$authHeader) {
return $this->reject(403, 'Access Forbidden');
}
$result = JWTToken::validateJWT($authHeader);
if ($result['status'] !== true) {
return $this->reject(401, $result['message']);
}
$decoded = $result['decoded'];
$id = $decoded['id'] ?? null;
if (!$id) {
return $this->reject(401, 'Invalid token payload');
}
if (isset($decoded['emp_code'])) {
$model = new EmployeeModel();
} else {
$model = new LevelContactModel();
$id = $decoded['pre_hr_id'] ?? null;
}
$user = $model->find($id);
if (!$user || $user['token_time_out'] <= time()) {
$model->update($id, ['token_time_out' => null]);
return $this->reject(401, 'Token expired');
}
// Refresh sliding expiration
$model->update($id, [
'token_time_out' => time() + getenv('TOKENTIMEOUT')
]);
return true;
}
private function reject(int $code, string $message)
{
return service('response')
->setStatusCode($code)
->setJSON(['status' => $code, 'message' => $message])
->send();
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)

View File

@ -5,12 +5,29 @@ use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use App\Libraries\AuthLogout;
class AuthMVC implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
if (!check_session() && !check_cookie()) {
return redirect()->to(base_url('/login'));
if (!check_session())
{
return AuthLogout::logout();
}
// if (!check_cookie())
// {
// return AuthLogout::logout();
// }
// Fingerprint validation
$fp = hash('sha256',
$request->getUserAgent()->getAgentString() . '|' . $request->getIPAddress()
);
if (session()->get('fingerprint') !== $fp) {
return AuthLogout::logout();
}
}

View File

@ -107,11 +107,11 @@ class Cors implements FilterInterface
);
}
$this->log('CORS filter initialized', [
'allowed_origins' => $this->allowedOrigins,
'allow_credentials' => $this->allowCredentials,
'allowed_methods' => $this->allowedMethods,
]);
// $this->log('CORS filter initialized', [
// 'allowed_origins' => $this->allowedOrigins,
// 'allow_credentials' => $this->allowCredentials,
// 'allowed_methods' => $this->allowedMethods,
// ]);
}
/**
@ -143,7 +143,7 @@ class Cors implements FilterInterface
// If wildcard present in configuration, allow any origin
if (in_array('*', $this->allowedOrigins, true)) {
$this->log('Origin allowed: wildcard match', ['origin' => $origin]);
// $this->log('Origin allowed: wildcard match', ['origin' => $origin]);
return true;
}
@ -159,10 +159,10 @@ class Cors implements FilterInterface
// 1. Exact match (including scheme and port)
// Example: https://example.com matches https://example.com
if (strcasecmp($allowed, $origin) === 0) {
$this->log('Origin allowed: exact match', [
'origin' => $origin,
'matched_rule' => $allowed
]);
// $this->log('Origin allowed: exact match', [
// 'origin' => $origin,
// 'matched_rule' => $allowed
// ]);
return true;
}
@ -178,11 +178,11 @@ class Cors implements FilterInterface
// Check if origin host ends with the allowed root domain
if ($originHost === $allowedRoot || str_ends_with($originHost, '.' . $allowedRoot)) {
$this->log('Origin allowed: wildcard subdomain match', [
'origin' => $origin,
'matched_rule' => $allowed,
'origin_host' => $originHost
]);
// $this->log('Origin allowed: wildcard subdomain match', [
// 'origin' => $origin,
// 'matched_rule' => $allowed,
// 'origin_host' => $originHost
// ]);
return true;
}
}
@ -191,11 +191,11 @@ class Cors implements FilterInterface
// Example: example.com matches both http://example.com and https://example.com
else {
if (strcasecmp($allowed, $originHost) === 0) {
$this->log('Origin allowed: host match (scheme-less)', [
'origin' => $origin,
'matched_rule' => $allowed,
'origin_host' => $originHost
]);
// $this->log('Origin allowed: host match (scheme-less)', [
// 'origin' => $origin,
// 'matched_rule' => $allowed,
// 'origin_host' => $originHost
// ]);
return true;
}
}
@ -320,11 +320,11 @@ class Cors implements FilterInterface
// Preflight is sent by browsers before actual cross-origin requests
// to check if the actual request is safe to send
if ($method === 'OPTIONS') {
$this->log('Preflight request received', [
'origin' => $origin,
'method' => $method,
'uri' => (string) $request->getUri()
]);
// $this->log('Preflight request received', [
// 'origin' => $origin,
// 'method' => $method,
// 'uri' => (string) $request->getUri()
// ]);
// Validate origin - reject if not allowed
if (empty($origin) || !$this->isOriginAllowed($origin)) {
@ -346,10 +346,10 @@ class Cors implements FilterInterface
$response->setStatusCode(204);
$response->setBody('');
$this->log('Preflight approved', [
'origin' => $origin,
'allowed_methods' => $this->allowedMethods
]);
// $this->log('Preflight approved', [
// 'origin' => $origin,
// 'allowed_methods' => $this->allowedMethods
// ]);
return $response;
}
@ -392,10 +392,10 @@ class Cors implements FilterInterface
// Add CORS headers to the response
$this->addCorsHeaders($response, $request, $origin, false);
$this->log('CORS headers added to response', [
'origin' => $origin,
'status' => $response->getStatusCode()
]);
// $this->log('CORS headers added to response', [
// 'origin' => $origin,
// 'status' => $response->getStatusCode()
// ]);
}
/**

View File

@ -0,0 +1,147 @@
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
use Config\Services;
use finfo;
class GlobalPostFileUploadGuard implements FilterInterface
{
/**
* Max file size (in bytes) 25MB
*/
protected int $maxFileSize = 25 * 1024 * 1024;
/**
* Allowed MIME types mapped to extensions
*/
protected array $allowedMimeMap = [
'image/jpeg' => ['jpg', 'jpeg'],
'image/png' => ['png'],
'image/gif' => ['gif'],
'image/webp' => ['webp'],
'image/svg+xml' => ['svg'],
'application/pdf' => ['pdf'],
'application/msword' => ['doc'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['docx'],
'application/vnd.oasis.opendocument.text' => ['odt'],
'text/rtf' => ['rtf'],
'application/rtf' => ['rtf'],
'application/vnd.ms-excel' => ['xls'],
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => ['xlsx'],
'application/vnd.oasis.opendocument.spreadsheet' => ['ods'],
'text/csv' => ['csv'],
'application/csv' => ['csv'],
'text/plain' => ['txt', 'csv'],
];
protected array $blockedExtensions = [
'php', 'phtml', 'pht', 'phar', 'php3', 'php4', 'php5', 'php7', 'php8', 'phps',
'cgi', 'fcgi', 'pl', 'py', 'rb', 'lua', 'tcl', 'go', 'rs', 'jar', 'class',
'exe', 'dll', 'com', 'bat', 'cmd', 'msi', 'vbs', 'ps1', 'scr',
'sh', 'bash', 'zsh', 'apk', 'app', 'deb', 'rpm', 'bin', 'run',
'js', 'mjs', 'jsp', 'asp', 'aspx', 'cer', 'swf',
'env', 'ini', 'user.ini', 'htaccess', 'htpasswd', 'conf', 'config', 'log', 'sql',
'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'iso',
'lnk', 'url', 'reg', 'sys', 'drv', 'vxd', 'tmp', 'bak', 'old', 'backup', 'key', 'pem'
];
public function before(RequestInterface $request, $arguments = null)
{
if ($request->getMethod() !== 'post') {
return;
}
$files = $request->getFiles();
if (empty($files)) {
return;
}
foreach ($files as $inputName => $fileData) {
$this->validateFileInput($fileData, $inputName);
}
}
private function validateFileInput($fileData, string $inputName): void
{
if (is_array($fileData)) {
foreach ($fileData as $file) {
$this->validateSingleFile($file, $inputName);
}
} else {
$this->validateSingleFile($fileData, $inputName);
}
}
private function validateSingleFile($file, string $inputName): void
{
$request = Services::request();
$clientIp = $request->getIPAddress();
$uri = $request->getUri()->getPath();
if (!$file->isValid()) {
if ($file->getError() === UPLOAD_ERR_INI_SIZE || $file->getError() === UPLOAD_ERR_FORM_SIZE) {
$this->block("File exceeds server-side size limit", $clientIp, $uri, $inputName, $file->getClientName(), 'unknown', 'unknown', 0);
}
return;
}
$originalName = $file->getClientName();
$extension = strtolower($file->getExtension());
$mime = $file->getMimeType();
$size = $file->getSize();
// --- 1. Fixed Null Byte & Path Traversal Check ---
if (preg_match('/\0|[\/\\\]/', $originalName)) {
$this->block("Malicious filename characters", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
// --- 2. Double Extension Attack Check ---
if (preg_match('/\.(php|phtml|phar|exe|sh|bat|cmd|js|jsp|asp|aspx|py|pl)\./i', $originalName)) {
$this->block("Double extension attack", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
// --- 3. Forbidden Extension ---
if (in_array($extension, $this->blockedExtensions, true)) {
$this->block("Forbidden extension", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
// --- 4. File Size Limit ---
if ($size > $this->maxFileSize) {
$this->block("File too large", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
// --- 5. MIME Allow-list Check ---
if (!array_key_exists($mime, $this->allowedMimeMap)) {
$this->block("MIME type not allowed ($mime)", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
// --- 6. MIME-Extension Consistency ---
if (!in_array($extension, $this->allowedMimeMap[$mime], true)) {
$this->block("MIME-extension mismatch", $clientIp, $uri, $inputName, $originalName, $mime, $extension, $size);
}
}
private function block(string $reason, string $ip, string $uri, string $field, string $filename, string $mime, string $ext, int $size): void
{
log_message('critical',
'[UPLOAD_BLOCKED] {reason} | IP: {ip} | URI: {uri} | Field: {field} | File: {file} | MIME: {mime} | EXT: {ext} | SIZE: {size}',
['reason'=>$reason, 'ip'=>$ip, 'uri'=>$uri, 'field'=>$field, 'file'=>$filename, 'mime'=>$mime, 'ext'=>$ext, 'size'=>$size]
);
$response = Services::response();
$response->setStatusCode(403)
->setJSON([
'status' => 'error',
'message' => 'File upload rejected: Security policy violation.',
'debug' => (ENVIRONMENT === 'development') ? $reason : null
])
->send();
exit;
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {}
}

View File

@ -0,0 +1,130 @@
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;
class SecurityInputFilter implements FilterInterface
{
/**
* High-confidence XSS patterns only
* (low false-positive set)
**/
protected array $xssPatterns = [
// Script execution
'/<\s*script\b/i',
'/<\/\s*script\s*>/i',
// JavaScript execution vectors
'/javascript\s*:/i',
'/vbscript\s*:/i',
'/data\s*:\s*text\/html/i',
// Inline event handlers (strong signal)
'/on\w+\s*=\s*["\']?/i',
// Dangerous HTML tags
'/<\s*iframe\b/i',
'/<\s*object\b/i',
'/<\s*embed\b/i',
'/<\s*applet\b/i',
'/<\s*img\b/i',
// Image-based execution
'/<\s*img\b[^>]*on\w+/i',
// SVG-based execution (modern bypass)
'/<\s*svg\b/i',
'/<\s*math\b/i',
// Meta refresh redirect
'/<\s*meta\b[^>]*http-equiv\s*=\s*["\']?refresh/i',
// HTML injection via src/href
'/<\s*\w+\b[^>]*(src|href)\s*=\s*["\']?\s*(javascript|data)\s*:/i'
];
public function before(RequestInterface $request, $arguments = null)
{
$logger = Services::mylogger();
$response = Services::response();
// Collect all user-controlled input
$inputs = array_merge(
$request->getGet(),
$request->getPost()
);
if (empty($inputs)) {
return;
}
foreach ($inputs as $field => $value) {
if (is_array($value)) {
$value = json_encode($value);
}
// Step 1: Canonicalization (VERY IMPORTANT)
$canonical = $this->canonicalize($value);
// Step 2: Trim (hygiene)
$canonical = trim($canonical);
// Step 3: Detection (signal-only)
if ($this->detectXss($canonical)) {
// 🔐 Log intent, not data
$logger->logme('critical','SECURITY_BLOCKED_REQUEST - '. json_encode([
'ip' => $request->getIPAddress(),
'method' => $request->getMethod(),
'uri' => current_url(),
'field' => $field,
'attack' => 'XSS_PATTERN',
'length' => strlen($canonical),
'hash' => hash('sha256', $canonical),
]));
// ⛔ Block request
return $response
->setStatusCode(403)
->setJSON([
'status' => 403,
'error' => 'Forbidden',
'message' => 'Malicious input detected'
]);
}
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// no-op
}
/**
* Canonicalization prevents encoded bypass
*/
private function canonicalize(string $value): string
{
$value = urldecode($value);
$value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// Remove invisible control characters
return preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
}
private function detectXss(string $value): bool
{
foreach ($this->xssPatterns as $pattern) {
if (preg_match($pattern, $value)) {
return true;
}
}
return false;
}
}

View File

@ -4,24 +4,175 @@ namespace App\Helpers;
class HttpRequestHelper
{
public static function getRequestInfo()
public static function getRequestInfo(): array
{
$request = service('request');
$uaString = $request->getHeaderLine('User-Agent');
// Detect platform & browser using robust fallback logic
[$platform, $browser] = self::detectFromUserAgent($uaString);
$data = [
'ip' => $request->getIPAddress(),
'platform' => $request->getUserAgent()->getPlatform(),
'browser' => ($request->getUserAgent()->getBrowser().' '.$request->getUserAgent()->getVersion()),
'method' => $request->getMethod(),
'endpoint' => $request->uri->getPath(),
'getparams' => $request->uri->getSegments(),
'postparams' => $request->getPost()
'ip' => $request->getIPAddress(),
'platform' => $platform,
'browser' => $browser,
'method' => strtoupper($request->getMethod()),
'endpoint' => $request->uri->getPath(),
'getparams' => json_encode($request->uri->getSegments(), JSON_UNESCAPED_UNICODE),
'postparams' => self::sanitizePostForLog($request->getPost()),
];
$data['method'] = (isset($data['method']) ? strtoupper($data['method']) : $data['method']);
$data['getparams'] = is_array($data['getparams']) ? json_encode($data['getparams']) : $data['getparams'];
$data['postparams'] = is_array($data['postparams']) ? json_encode($data['postparams']) : $data['postparams'];
return $data;
}
public static function add($payload)
{return $payload['a'] + $payload['b'];}
/**
* Detect platform & browser from UA string (reliable fallback)
*/
private static function detectFromUserAgent(string $ua): array
{
$uaLower = strtolower($ua);
// =========================
// PLATFORM DETECTION
// =========================
$platform = 'Unknown';
if (str_contains($uaLower, 'windows nt 11') || str_contains($uaLower, 'windows 11')) {
$platform = 'Windows 11';
} elseif (str_contains($uaLower, 'windows nt 10')) {
$platform = 'Windows 10';
} elseif (str_contains($uaLower, 'windows nt 6.3')) {
$platform = 'Windows 8.1';
} elseif (str_contains($uaLower, 'windows nt 6.2')) {
$platform = 'Windows 8';
} elseif (str_contains($uaLower, 'windows nt 6.1')) {
$platform = 'Windows 7';
} elseif (str_contains($uaLower, 'windows nt 6.0')) {
$platform = 'Windows Vista';
} elseif (str_contains($uaLower, 'windows nt 5.1') || str_contains($uaLower, 'windows xp')) {
$platform = 'Windows XP';
} elseif (str_contains($uaLower, 'android')) {
$platform = 'Android';
} elseif (str_contains($uaLower, 'iphone')) {
$platform = 'iOS (iPhone)';
} elseif (str_contains($uaLower, 'ipad')) {
$platform = 'iOS (iPad)';
} elseif (str_contains($uaLower, 'ipod')) {
$platform = 'iOS (iPod)';
} elseif (str_contains($uaLower, 'mac os') || str_contains($uaLower, 'macintosh')) {
$platform = 'Mac OS';
} elseif (str_contains($uaLower, 'cros')) {
$platform = 'Chrome OS';
} elseif (str_contains($uaLower, 'linux')) {
$platform = 'Linux';
} elseif (str_contains($uaLower, 'freebsd')) {
$platform = 'FreeBSD';
} elseif (str_contains($uaLower, 'openbsd')) {
$platform = 'OpenBSD';
} elseif (str_contains($uaLower, 'netbsd')) {
$platform = 'NetBSD';
} elseif (str_contains($uaLower, 'unix')) {
$platform = 'Unix';
} elseif (str_contains($uaLower, 'symbian')) {
$platform = 'Symbian';
} elseif (str_contains($uaLower, 'blackberry')) {
$platform = 'BlackBerry';
} elseif (str_contains($uaLower, 'tizen')) {
$platform = 'Tizen';
} elseif (str_contains($uaLower, 'webos')) {
$platform = 'WebOS';
} elseif (str_contains($uaLower, 'kaios')) {
$platform = 'KaiOS';
} elseif (str_contains($uaLower, 'harmonyos')) {
$platform = 'HarmonyOS';
} elseif (str_contains($uaLower, 'watchos')) {
$platform = 'watchOS';
} elseif (str_contains($uaLower, 'tv os') || str_contains($uaLower, 'tvos')) {
$platform = 'tvOS';
}
// =========================
// BROWSER / CLIENT DETECTION
// =========================
$browser = 'Unknown';
// Bots & tools first
if (preg_match('/googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|sogou|exabot|facebot|ia_archiver/i', $ua)) {
$browser = 'Search Bot';
} elseif (preg_match('/postman/i', $ua)) {
$browser = 'Postman';
} elseif (preg_match('/insomnia/i', $ua)) {
$browser = 'Insomnia';
} elseif (preg_match('/curl/i', $ua)) {
$browser = 'curl';
} elseif (preg_match('/wget/i', $ua)) {
$browser = 'wget';
}
// Real browsers
elseif (preg_match('/edg\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Edge ' . $m[1];
} elseif (preg_match('/opr\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Opera ' . $m[1];
} elseif (preg_match('/vivaldi\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Vivaldi ' . $m[1];
} elseif (preg_match('/brave\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Brave ' . $m[1];
} elseif (preg_match('/chrome\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Chrome ' . $m[1];
} elseif (preg_match('/firefox\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Firefox ' . $m[1];
} elseif (preg_match('/safari\/([\d\.]+)/i', $ua, $m)) {
$browser = 'Safari ' . $m[1];
} elseif (preg_match('/msie\s([\d\.]+)/i', $ua, $m) || preg_match('/trident\/.*rv:([\d\.]+)/i', $ua, $m)) {
$browser = 'Internet Explorer ' . $m[1];
}
// In-app browsers
elseif (preg_match('/fbav|fban/i', $ua)) {
$browser = 'Facebook In-App Browser';
} elseif (preg_match('/instagram/i', $ua)) {
$browser = 'Instagram In-App Browser';
} elseif (preg_match('/linkedinapp/i', $ua)) {
$browser = 'LinkedIn In-App Browser';
} elseif (preg_match('/twitter/i', $ua)) {
$browser = 'Twitter/X In-App Browser';
}
return [$platform, $browser];
}
/**
* Remove sensitive fields before logging POST
*/
private static function sanitizePostForLog(array $post): string
{
if (empty($post)) {
return json_encode([]);
}
$sensitiveKeys = [
'password', 'pass', 'pwd',
'token', 'access_token', 'refresh_token',
'secret', 'api_key', 'authorization',
'otp', 'pin'
];
foreach ($post as $k => $v) {
foreach ($sensitiveKeys as $sk) {
if (stripos($k, $sk) !== false) {
$post[$k] = '***MASKED***';
}
}
}
return json_encode($post, JSON_UNESCAPED_UNICODE);
}
public static function add($payload)
{
return $payload['a'] + $payload['b'];
}
}

View File

@ -18,9 +18,13 @@ use App\Models\LevelContactModel;
class JWTToken
{
private const ALLOWED_ALG = 'HS512';
public static function encode($data =null)
{
$secret_Key ="secret";
$secret_Key = env('JWT_SECRET');
$request_data = (array)$data;
@ -47,30 +51,79 @@ class JWTToken
}
}
public static function validateJWT($jwt)
// public static function validateJWT($jwt)
// {
// $jwtParts = explode(' ', $jwt);
// // print_r($jwtParts);
// if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') {
// return false;
// }
// $token = $jwtParts[1];
// try {
// $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS512'));
// return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]);
// } catch (ExpiredException $e) {
// return json_encode(['status' => false, 'message' => 'Token has expired']);
// } catch (BeforeValidException $e) {
// return json_encode(['status' => false, 'message' => 'Token is not yet valid']);
// } catch (SignatureInvalidException $e) {
// return json_encode(['status' => false, 'message' => 'Token signature is invalid']);
// } catch (\Exception $e) {
// return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']);
// }
// }
public static function validateJWT(string $authHeader)
{
$jwtParts = explode(' ', $jwt);
// 1⃣ Validate Authorization header
if (!preg_match('/^Bearer\s(\S+)$/', $authHeader, $matches)) {
return ['status' => false, 'message' => 'Invalid Authorization header'];
}
// print_r($jwtParts);
if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') {
return false;
}
$token = $matches[1];
$token = $jwtParts[1];
// 2⃣ Decode JWT header manually
$jwtParts = explode('.', $token);
if (count($jwtParts) !== 3) {
return ['status' => false, 'message' => 'Malformed JWT'];
}
try {
$decoded = JWT::decode($token, new Key("secret", 'HS512'));
return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]);
} catch (ExpiredException $e) {
return json_encode(['status' => false, 'message' => 'Token has expired']);
} catch (BeforeValidException $e) {
return json_encode(['status' => false, 'message' => 'Token is not yet valid']);
} catch (SignatureInvalidException $e) {
return json_encode(['status' => false, 'message' => 'Token signature is invalid']);
} catch (\Exception $e) {
return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']);
}
$header = json_decode(base64_decode(strtr($jwtParts[0], '-_', '+/')), true);
// 3⃣ Reject missing or NONE algorithm
if (
empty($header['alg']) ||
$header['alg'] === 'none' ||
$header['alg'] !== self::ALLOWED_ALG
) {
return ['status' => false, 'message' => 'Invalid or unsupported JWT algorithm'];
}
// 4⃣ Enforce signature validation
try {
$decoded = JWT::decode(
$token,
new Key(env('JWT_SECRET'), self::ALLOWED_ALG)
);
return [
'status' => true,
'decoded' => (array) $decoded
];
} catch (ExpiredException $e) {
return ['status' => false, 'message' => 'Token expired'];
} catch (BeforeValidException $e) {
return ['status' => false, 'message' => 'Token not yet valid'];
} catch (SignatureInvalidException $e) {
return ['status' => false, 'message' => 'Invalid token signature'];
} catch (\Exception $e) {
return ['status' => false, 'message' => 'Token validation failed'];
}
}

View File

@ -21,7 +21,7 @@ if(!function_exists('check_cookie')){
'userProfile' => $value['userProfile'],
'user_team' => $user_team,
];
set_session_data($session_data);
// set_session_data($session_data);
// $this->getUserDeviceInfo($user->id, 'NhanceUser');
// return redirect()->to(base_url('/dashboard/view'));
return true;

View File

@ -677,6 +677,32 @@ if (!function_exists('check_pay_by_employee_or_company')) {
}
if (!function_exists('canSendOtp')) {
function canSendOtp(array $row, int $limitSeconds = 60): array
{
// If OTP does not exist → allow
if (empty($row['otp']) || empty($row['updated_at'])) {
return ['allowed' => true];
}
$lastUpdated = strtotime($row['updated_at']);
$currentTime = time();
// Calculate expiry time
$allowedAfter = $lastUpdated + $limitSeconds;
// If still within limit → block
if ($currentTime < $allowedAfter) {
return [
'allowed' => false,
'retry_after' => $allowedAfter - $currentTime
];
}
return ['allowed' => true];
}
}
if (!function_exists('getLatestGMCPolicy')) {
function getLatestGMCPolicy(array $empPolicy)
@ -709,3 +735,24 @@ if (!function_exists('getLatestGMCPolicy')) {
}
if (!function_exists('validateExcelFile')) {
function validateExcelFile($file)
{
$allowed = [
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.oasis.opendocument.spreadsheet'
];
// if ($file->getError() !== UPLOAD_ERR_OK) return 'Upload error';
// if ($file->getSize() > (16 * 1024 * 1024)) return 'File too large';
// if (!in_array($file->getClientMimeType(), $allowed, true)) return 'Invalid file type';
if ($file->getError() !== UPLOAD_ERR_OK) return false;
if ($file->getSize() > (16 * 1024 * 1024)) return false;
if (!in_array($file->getClientMimeType(), $allowed, true)) return false;
return true;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Libraries;
use CodeIgniter\HTTP\RedirectResponse;
class AuthLogout
{
public static function logout(): RedirectResponse
{
$session = session();
// Regenerate session ID (kills fixation)
$session->regenerate(true);
// Destroy CI session
$session->destroy();
// Kill PHP session cookie safely
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(
session_name(), // DO NOT hardcode cookie name
null,
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}
session_write_close();
// Redirect with anti-cache headers
return redirect()->to(base_url('login'))
->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->setHeader('Pragma', 'no-cache')
->setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
}
}

View File

@ -23,6 +23,7 @@ class FileModel extends Model
"updated_by",
"enrollment_open_date",
"enrollment_close_date",
"hr_id",
];