Merge branch 'dev' of bitbucket.org:jubilian/nhance into dev
This commit is contained in:
commit
0dc7d37f84
@ -108,3 +108,5 @@ CORS_MAX_AGE=7200
|
|||||||
CORS_DEBUG=true
|
CORS_DEBUG=true
|
||||||
|
|
||||||
APP_SIGNATURE =
|
APP_SIGNATURE =
|
||||||
|
TOKENTIMEOUT =
|
||||||
|
JWT_SECRET =
|
||||||
157
app/Config/Acl.php
Normal file
157
app/Config/Acl.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?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' => [ ADMIN_ROLE_ID,HEAD_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' => []
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== POLICY TRANSACTION / BDS =====================
|
||||||
|
'#^/policy_tranction#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||||
|
],
|
||||||
|
'#^/bds_upload#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== REPORTS =====================
|
||||||
|
'#^/bdsReport#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== PAYOUT / COMMISSION =====================
|
||||||
|
'#^/payout#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||||
|
],
|
||||||
|
'#^/commission#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== CLAIMS / TICKETS =====================
|
||||||
|
'#^/ticket#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [CLAIMS_TEAM_ID]
|
||||||
|
],
|
||||||
|
'#^/claim_mis#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [CLAIMS_TEAM_ID]
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== LEADS / SALES =====================
|
||||||
|
'#^/leads#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||||
|
],
|
||||||
|
'#^/rfq#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
|
'teams' => [SALES_TEAM_ID]
|
||||||
|
],
|
||||||
|
'#^/sales#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID],
|
||||||
|
'teams' => [SALES_TEAM_ID]
|
||||||
|
],
|
||||||
|
|
||||||
|
// ===================== CMS / CONTENT =====================
|
||||||
|
'#^/add_image_index#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||||
|
'teams' => []
|
||||||
|
],
|
||||||
|
'#^/frontend_content#' => [
|
||||||
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||||
|
'teams' => []
|
||||||
|
],
|
||||||
|
'#^/FAQ#' => [
|
||||||
|
'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],
|
||||||
|
|
||||||
|
// ===================== WEBHOOKS / 3RD PARTY =====================
|
||||||
|
'#^/dispatchWebhookData#' => ['public' => true],
|
||||||
|
'#^/retrieveWebhookData#' => ['public' => true],
|
||||||
|
// '#^/ICICI#' => ['public' => true],
|
||||||
|
// '#^/Vidal#' => ['public' => true],
|
||||||
|
// '#^/MediAssist#' => ['public' => true],
|
||||||
|
|
||||||
|
// ===================== CLI =====================
|
||||||
|
'#^/cli/#' => ['public' => true],
|
||||||
|
|
||||||
|
// ===================== DEFAULT DENY (ZERO TRUST) =====================
|
||||||
|
'#^/#' => [
|
||||||
|
'roles' => [ADMIN_ROLE_ID],
|
||||||
|
'teams' => []
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -101,6 +101,6 @@ class Autoload extends AutoloadConfig
|
|||||||
* @phpstan-var list<string>
|
* @phpstan-var list<string>
|
||||||
*/
|
*/
|
||||||
public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload',
|
public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload',
|
||||||
'excel_import_export', 'file', 'drive','ExcelSanitizeHelper', 'api_helper','exception','sms_helper'
|
'excel_import_export', 'file', 'drive','ExcelSanitizeHelper', 'api_helper','exception','sms_helper','sanitizeInputArrayAdvanced'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ use App\Filters\VerifyAppSignature;
|
|||||||
use App\Filters\Cors;
|
use App\Filters\Cors;
|
||||||
use App\Filters\SecurityInputFilter;
|
use App\Filters\SecurityInputFilter;
|
||||||
use App\Filters\GlobalPostFileUploadGuard;
|
use App\Filters\GlobalPostFileUploadGuard;
|
||||||
|
use App\Filters\AclFilter;
|
||||||
|
|
||||||
use App\Filters\AuthJWT;
|
use App\Filters\AuthJWT;
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ class Filters extends BaseConfig
|
|||||||
'Cors' => Cors::class,
|
'Cors' => Cors::class,
|
||||||
'SecurityInputFilter' => SecurityInputFilter::class,
|
'SecurityInputFilter' => SecurityInputFilter::class,
|
||||||
'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class,
|
'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class,
|
||||||
|
'AclFilter' => AclFilter::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,6 +62,7 @@ class Filters extends BaseConfig
|
|||||||
'before' => [
|
'before' => [
|
||||||
'HttpRequestLog' => ['except' => 'cli/*'],
|
'HttpRequestLog' => ['except' => 'cli/*'],
|
||||||
'Cors',
|
'Cors',
|
||||||
|
'AclFilter' => ['except' => 'login', 'logout', 'auth/*', 'oauth2callback', 'download-*', 'claim-form-download', 'claims-feedback-form', 'autobookstackLogin'],
|
||||||
'SecurityInputFilter',
|
'SecurityInputFilter',
|
||||||
'GlobalPostFileUploadGuard'
|
'GlobalPostFileUploadGuard'
|
||||||
// 'csrf',
|
// 'csrf',
|
||||||
|
|||||||
@ -736,14 +736,102 @@ class ClientController extends AdminController
|
|||||||
|
|
||||||
public function saveDeposit()
|
public function saveDeposit()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
|
||||||
|
'amount' => [
|
||||||
|
'rules' => 'required|numeric|greater_than_equal_to[0]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Amount is required',
|
||||||
|
'numeric' => 'Amount must be a valid number',
|
||||||
|
'greater_than_equal_to' => 'Amount cannot be negative',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'client_id' => [
|
||||||
|
'rules' => 'required|is_natural_no_zero',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Client ID is required',
|
||||||
|
'is_natural_no_zero' => 'Client ID must be a positive integer',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'insurer_id' => [
|
||||||
|
'rules' => 'required|is_natural_no_zero',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Insurer ID is required',
|
||||||
|
'is_natural_no_zero' => 'Insurer ID must be a positive integer',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'cd_ac_pk' => [
|
||||||
|
'rules' => 'required|is_natural_no_zero',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Account PK is required',
|
||||||
|
'is_natural_no_zero' => 'Account PK must be a positive integer',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'cd_ac_no' => [
|
||||||
|
'rules' => 'required|is_natural_no_zero',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Account number is required',
|
||||||
|
'is_natural_no_zero' => 'must be a positive integer',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'sub_type_id' => [
|
||||||
|
'rules' => 'required|is_natural_no_zero',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Sub type ID is required',
|
||||||
|
'is_natural_no_zero' => 'Sub type ID must be a positive integer',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'description' => [
|
||||||
|
'rules' => 'required|string|min_length[3]|max_length[255]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Description is required',
|
||||||
|
'string' => 'Description must be text',
|
||||||
|
'min_length' => 'Description must be at least 3 characters',
|
||||||
|
'max_length' => 'Description must not exceed 255 characters',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
'transaction_type' => [
|
||||||
|
'rules' => 'required|in_list[Credit,Debit]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Transaction type is required',
|
||||||
|
'in_list' => 'Transaction type must be either credit or debit',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
if (! $this->validate($rules)) {
|
||||||
|
return $this->response
|
||||||
|
->setStatusCode(400)
|
||||||
|
->setJSON([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Input validation failed',
|
||||||
|
'errors' => $this->validator->getErrors()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sanitize the post params
|
||||||
|
$post_data = $this->request->getPost();
|
||||||
|
$sanitized_post_data = sanitizeInputArrayAdvanced($post_data);
|
||||||
// Retrieve form data from POST request
|
// Retrieve form data from POST request
|
||||||
$loggedInUserID = get_session_userid();
|
$loggedInUserID = get_session_userid();
|
||||||
|
// print_rr($sanitized_post_data);die();
|
||||||
|
$client_id = $sanitized_post_data['client_id'];
|
||||||
|
$insurer_id = $sanitized_post_data['insurer_id'];
|
||||||
|
$record_date = $sanitized_post_data['record_date'];
|
||||||
|
$cd_ac_pk = $sanitized_post_data['cd_ac_pk'];
|
||||||
|
$cd_ac_no = $sanitized_post_data['cd_ac_no'];
|
||||||
|
|
||||||
|
|
||||||
$client_id = $this->request->getPost('client_id');
|
|
||||||
$insurer_id = $this->request->getPost('insurer_id');
|
|
||||||
$record_date = $this->request->getPost('record_date');
|
|
||||||
$cd_ac_pk = $this->request->getPost('cd_ac_pk');
|
|
||||||
$cd_ac_no = $this->request->getPost('cd_ac_no');
|
|
||||||
|
|
||||||
// $CD_Account_Number = $this->CDMasterModel
|
// $CD_Account_Number = $this->CDMasterModel
|
||||||
// ->where('client_id', $client_id)
|
// ->where('client_id', $client_id)
|
||||||
@ -761,16 +849,16 @@ class ClientController extends AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'amount' => $this->request->getPost('amount'),
|
'amount' => $sanitized_post_data['amount'],
|
||||||
'sub_type_id' => $this->request->getPost('sub_type_id'),
|
'sub_type_id' => $sanitized_post_data['sub_type_id'],
|
||||||
'client_id' => $this->request->getPost('client_id'),
|
'client_id' => $sanitized_post_data['client_id'],
|
||||||
'client_policy_id' => null,
|
'client_policy_id' => null,
|
||||||
'cd_ac_no' => $cd_ac_no ?? null,
|
'cd_ac_no' => $cd_ac_no ?? null,
|
||||||
'cd_ac_pk' => $cd_ac_pk ?? null,
|
'cd_ac_pk' => $cd_ac_pk ?? null,
|
||||||
'endorsement_no' => null,
|
'endorsement_no' => null,
|
||||||
'insurer_id' => $this->request->getPost('insurer_id'),
|
'insurer_id' => $sanitized_post_data['insurer_id'],
|
||||||
'description' => $this->request->getPost('description'),
|
'description' => $sanitized_post_data['description'],
|
||||||
'transaction_type' => $this->request->getPost('transaction_type') ?: 'Credit',
|
'transaction_type' => $sanitized_post_data['transaction_type'] ?: 'Credit',
|
||||||
'updated_by' => 1,
|
'updated_by' => 1,
|
||||||
'record_date' => $record_date
|
'record_date' => $record_date
|
||||||
];
|
];
|
||||||
|
|||||||
156
app/Filters/AclFilter.php
Normal file
156
app/Filters/AclFilter.php
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
84
app/Helpers/sanitizeInputArrayAdvanced_helper.php
Normal file
84
app/Helpers/sanitizeInputArrayAdvanced_helper.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// use Normalizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High security sanitizer
|
||||||
|
* - Normalizes unicode
|
||||||
|
* - Removes control chars
|
||||||
|
* - Removes null bytes
|
||||||
|
* - Removes invisible unicode tricks
|
||||||
|
* - Strips dangerous HTML
|
||||||
|
* - Prevents polyglot payloads
|
||||||
|
*/
|
||||||
|
function sanitizeInputArrayAdvanced(array $data, array $htmlAllowedFields = []): array
|
||||||
|
{
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
|
||||||
|
if (is_array($v)) {
|
||||||
|
$data[$k] = sanitizeInputArrayAdvanced($v, $htmlAllowedFields);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_string($v)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Unicode normalization (prevents homoglyph attacks)
|
||||||
|
if (class_exists('Normalizer')) {
|
||||||
|
$v = \Normalizer::normalize($v, \Normalizer::FORM_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Remove NULL bytes & control chars
|
||||||
|
$v = preg_replace('/[\x00-\x1F\x7F]/u', '', $v);
|
||||||
|
|
||||||
|
// 3. Remove invisible unicode chars (zero width, etc)
|
||||||
|
$v = preg_replace('/[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}-\x{206F}]/u', '', $v);
|
||||||
|
|
||||||
|
// 4. Decode HTML entities (so hidden payloads are exposed)
|
||||||
|
$v = html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||||
|
|
||||||
|
// 5. Trim
|
||||||
|
$v = trim($v);
|
||||||
|
|
||||||
|
// 6. If this field is NOT allowed to contain HTML → strip aggressively
|
||||||
|
if (!in_array($k, $htmlAllowedFields, true)) {
|
||||||
|
|
||||||
|
// Remove all tags
|
||||||
|
$v = strip_tags($v);
|
||||||
|
|
||||||
|
// Kill any leftover JS protocol
|
||||||
|
$v = preg_replace('/(javascript:|data:|vbscript:)/i', '', $v);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// This is HTML-allowed field → run HTML sanitizer
|
||||||
|
$v = sanitizeTrustedHtml($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data[$k] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeTrustedHtml(string $html): string
|
||||||
|
{
|
||||||
|
// Allowed tags for email templates
|
||||||
|
$allowedTags = '<p><br><b><strong><i><u><em><ul><ol><li><table><thead><tbody><tr><td><th><a><img><div><span><h1><h2><h3><h4><h5><h6>';
|
||||||
|
|
||||||
|
// Strip all other tags
|
||||||
|
$html = strip_tags($html, $allowedTags);
|
||||||
|
|
||||||
|
// Remove event handlers like onclick, onerror, etc
|
||||||
|
$html = preg_replace('/\son\w+="[^"]*"/i', '', $html);
|
||||||
|
$html = preg_replace("/\son\w+='[^']*'/i", '', $html);
|
||||||
|
|
||||||
|
// Remove javascript: and data:
|
||||||
|
$html = preg_replace('/(javascript:|vbscript:|data:)/i', '', $html);
|
||||||
|
|
||||||
|
// Remove iframe, object, embed even if sneaked in
|
||||||
|
$html = preg_replace('/<(iframe|object|embed|script|style)[^>]*>.*?<\/\1>/is', '', $html);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
@ -118,7 +118,8 @@ if (!function_exists('check_role')) {
|
|||||||
{
|
{
|
||||||
// $ci =& get_instance();
|
// $ci =& get_instance();
|
||||||
$session = \Config\Services::session();
|
$session = \Config\Services::session();
|
||||||
return $session->get('role');
|
return $role_id = isset(get_session_userdata()->role) ? get_session_userdata()->role : null;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class AuthLogout
|
|||||||
|
|
||||||
setcookie(
|
setcookie(
|
||||||
session_name(), // DO NOT hardcode cookie name
|
session_name(), // DO NOT hardcode cookie name
|
||||||
'',
|
null,
|
||||||
time() - 42000,
|
time() - 42000,
|
||||||
$params['path'],
|
$params['path'],
|
||||||
$params['domain'],
|
$params['domain'],
|
||||||
|
|||||||
@ -162,7 +162,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="text">
|
<div class="text" style="text-align: center;">
|
||||||
<h1><?= isset($message) && !empty($message) ? $message : '404 PAGE NOT FOUND' ?></h1>
|
<h1><?= isset($message) && !empty($message) ? $message : '404 PAGE NOT FOUND' ?></h1>
|
||||||
</div>
|
</div>
|
||||||
<div><img class="image" src="https://omjsblog.files.wordpress.com/2023/07/errorimg.png" alt=""></div>
|
<div><img class="image" src="https://omjsblog.files.wordpress.com/2023/07/errorimg.png" alt=""></div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user