mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 07:01:16 +00:00
Security: enforce per-user client restrictions on client, financial and bulk handlers and per-client reports
Agents restricted from a client could still edit, archive, restore or delete it, run bulk actions on it (edits, tags, archive, email, tickets) and add credits. Every selected client is now checked before anything is written. Expense, trip and recurring expense edits now check the client the record already belongs to, not just the submitted one. Recurring expense delete, revenue edit/delete and task approval handlers gained the same check. Per-client reports and the dashboard payment/ticket lists now hide clients the agent is denied. Company-wide totals are unchanged.
This commit is contained in:
@@ -123,6 +123,7 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
SELECT client_name, invoice_number, invoice_prefix, payment_amount, payment_date FROM payments
|
||||
JOIN invoices ON payment_invoice_id = invoice_id
|
||||
JOIN clients ON invoice_client_id = client_id
|
||||
WHERE 1=1 " . clientScopeSql('invoice_client_id') . "
|
||||
ORDER BY payment_id DESC LIMIT 5
|
||||
");
|
||||
|
||||
@@ -607,6 +608,7 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
LEFT JOIN contacts ON ticket_contact_id = contact_id
|
||||
WHERE ticket_assigned_to = $session_user_id
|
||||
AND ticket_closed_at IS NULL
|
||||
" . clientScopeSql('ticket_client_id') . "
|
||||
ORDER BY ticket_number DESC
|
||||
");
|
||||
?>
|
||||
|
||||
@@ -140,6 +140,8 @@ if (isset($_POST['edit_client'])) {
|
||||
|
||||
$client_id = intval($_POST['client_id']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// Update client
|
||||
mysqli_query($mysqli, "UPDATE clients SET client_name = '$name', client_type = '$type', client_website = '$website', client_referral = '$referral', client_rate = $rate, client_net_terms = $net_terms, client_tax_id_number = '$tax_id_number', client_lead = $lead, client_abbreviation = '$abbreviation', client_notes = '$notes' WHERE client_id = $client_id");
|
||||
|
||||
@@ -212,6 +214,8 @@ if (isset($_GET['archive_client'])) {
|
||||
|
||||
$client_id = intval($_GET['archive_client']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// Archive client
|
||||
mysqli_query($mysqli, "UPDATE clients SET client_archived_at = NOW() WHERE client_id = $client_id");
|
||||
|
||||
@@ -242,6 +246,8 @@ if (isset($_GET['restore_client'])) {
|
||||
|
||||
$client_id = intval($_GET['restore_client']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// Get Client Name
|
||||
$client_name = escapeSql(getFieldById('clients', $client_id, 'client_name'));
|
||||
|
||||
@@ -263,6 +269,8 @@ if (isset($_GET['delete_client'])) {
|
||||
|
||||
$client_id = intval($_GET['delete_client']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// Get Client Name
|
||||
$client_name = escapeSql(getFieldById('clients', $client_id, 'client_name'));
|
||||
|
||||
@@ -761,6 +769,11 @@ if (isset($_POST['bulk_add_client_ticket'])) {
|
||||
// Create ticket for each selected asset
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before creating any tickets
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
// Get a Asset Count
|
||||
$client_count = count($_POST['client_ids']);
|
||||
|
||||
@@ -844,6 +857,11 @@ if (isset($_POST['bulk_edit_client_industry'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach($_POST['client_ids'] as $client_id) {
|
||||
@@ -878,6 +896,11 @@ if (isset($_POST['bulk_edit_client_referral'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach($_POST['client_ids'] as $client_id) {
|
||||
@@ -912,6 +935,11 @@ if (isset($_POST['bulk_edit_client_hourly_rate'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach($_POST['client_ids'] as $client_id) {
|
||||
@@ -946,6 +974,11 @@ if (isset($_POST['bulk_edit_client_net_terms'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach($_POST['client_ids'] as $client_id) {
|
||||
@@ -978,6 +1011,11 @@ if (isset($_POST['bulk_assign_client_tags'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach($_POST['client_ids'] as $client_id) {
|
||||
@@ -1024,6 +1062,11 @@ if (isset($_POST['bulk_send_client_email']) && isset($_POST['client_ids'])) {
|
||||
$client_ids = array_map('intval', $_POST['client_ids']);
|
||||
$count = count($client_ids);
|
||||
|
||||
// Check every selected client before queuing anything
|
||||
foreach ($client_ids as $client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
// Email metadata
|
||||
$mail_from = escapeSql($_POST['mail_from']);
|
||||
$mail_from_name = escapeSql($_POST['mail_from_name']);
|
||||
@@ -1112,6 +1155,11 @@ if (isset($_POST['bulk_archive_clients'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
@@ -1148,6 +1196,11 @@ if (isset($_POST['bulk_unarchive_clients'])) {
|
||||
|
||||
if (isset($_POST['client_ids'])) {
|
||||
|
||||
// Check every selected client before changing any of them
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
enforceClientAccess(intval($client_id));
|
||||
}
|
||||
|
||||
$count = count($_POST['client_ids']);
|
||||
|
||||
foreach ($_POST['client_ids'] as $client_id) {
|
||||
|
||||
@@ -13,6 +13,9 @@ if (isset($_POST['add_credit'])) {
|
||||
enforceUserPermission('module_sales', 2);
|
||||
|
||||
$client_id = intval($_POST['client']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
$amount = floatval($_POST['amount']);
|
||||
$type = escapeSql($_POST['type']);
|
||||
$expire = escapeSql($_POST['expire']);
|
||||
|
||||
@@ -57,12 +57,18 @@ if (isset($_POST['edit_expense'])) {
|
||||
|
||||
require_once 'expense_model.php';
|
||||
|
||||
$expense_id = intval($_POST['expense_id']);
|
||||
|
||||
// Check the client the expense belongs to now, not just the one submitted
|
||||
$existing_client_id = intval(getFieldById('expenses', $expense_id, 'expense_client_id'));
|
||||
if ($existing_client_id) {
|
||||
enforceClientAccess($existing_client_id);
|
||||
}
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess();
|
||||
}
|
||||
|
||||
$expense_id = intval($_POST['expense_id']);
|
||||
|
||||
// Get old receipt
|
||||
$existing_file_name = escapeSql(getFieldById('expenses', $expense_id, 'expense_receipt'));
|
||||
|
||||
|
||||
@@ -63,6 +63,12 @@ if (isset($_POST['edit_recurring_expense'])) {
|
||||
$description = escapeSql($_POST['description']);
|
||||
$reference = escapeSql($_POST['reference']);
|
||||
|
||||
// Check the client the recurring expense belongs to now, not just the one submitted
|
||||
$existing_client_id = intval(getFieldById('recurring_expenses', $recurring_expense_id, 'recurring_expense_client_id'));
|
||||
if ($existing_client_id) {
|
||||
enforceClientAccess($existing_client_id);
|
||||
}
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess();
|
||||
}
|
||||
@@ -97,6 +103,10 @@ if (isset($_GET['delete_recurring_expense'])) {
|
||||
$recurring_expense_description = escapeSql($row['recurring_expense_description']);
|
||||
$client_id = intval($row['recurring_expense_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess();
|
||||
}
|
||||
|
||||
mysqli_query($mysqli,"DELETE FROM recurring_expenses WHERE recurring_expense_id = $recurring_expense_id");
|
||||
|
||||
logAudit("Recurring Expense", "Delete", "$session_name deleted recurring expense $recurring_expense_description", $client_id);
|
||||
|
||||
@@ -39,6 +39,12 @@ if (isset($_POST['edit_revenue'])) {
|
||||
enforceUserPermission('module_sales', 2);
|
||||
|
||||
$revenue_id = intval($_POST['revenue_id']);
|
||||
|
||||
$client_id = intval(getFieldById('revenues', $revenue_id, 'revenue_client_id'));
|
||||
if ($client_id) {
|
||||
enforceClientAccess();
|
||||
}
|
||||
|
||||
$date = escapeSql($_POST['date']);
|
||||
$amount = floatval($_POST['amount']);
|
||||
$account = intval($_POST['account']);
|
||||
@@ -65,6 +71,11 @@ if (isset($_GET['delete_revenue'])) {
|
||||
|
||||
$revenue_id = intval($_GET['delete_revenue']);
|
||||
|
||||
$client_id = intval(getFieldById('revenues', $revenue_id, 'revenue_client_id'));
|
||||
if ($client_id) {
|
||||
enforceClientAccess();
|
||||
}
|
||||
|
||||
// Get Revenue Details
|
||||
$revenue_description = escapeSql(getFieldById('revenues', $revenue_id, 'revenue_description'));
|
||||
|
||||
|
||||
@@ -364,6 +364,9 @@ if (isset($_GET['approve_ticket_task'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$client_id = intval(getFieldById('tickets', $ticket_id, 'ticket_client_id'));
|
||||
enforceClientAccess();
|
||||
|
||||
// Validate approver (deny)
|
||||
if ($required_user > 0 && $required_user !== $session_user_id) {
|
||||
flashAlert("You cannot approve that task", 'error');
|
||||
@@ -399,6 +402,11 @@ if (isset($_GET['delete_ticket_task_approver'])) {
|
||||
|
||||
$approval_id = intval($_GET['delete_ticket_task_approver']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT ticket_client_id FROM task_approvals LEFT JOIN tasks ON task_id = approval_task_id LEFT JOIN tickets ON ticket_id = task_ticket_id WHERE approval_id = $approval_id");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
$client_id = intval($row['ticket_client_id'] ?? 0);
|
||||
enforceClientAccess();
|
||||
|
||||
mysqli_query($mysqli, "DELETE FROM task_approvals WHERE approval_id = $approval_id");
|
||||
|
||||
logAudit("Task", "Delete", "$session_name deleted task approval request ($approval_id)", 0, 0);
|
||||
|
||||
@@ -40,6 +40,12 @@ if (isset($_POST['edit_trip'])) {
|
||||
|
||||
$trip_id = intval($_POST['trip_id']);
|
||||
|
||||
// Check the client the trip belongs to now, not just the one submitted
|
||||
$existing_client_id = intval(getFieldById('trips', $trip_id, 'trip_client_id'));
|
||||
if ($existing_client_id) {
|
||||
enforceClientAccess($existing_client_id);
|
||||
}
|
||||
|
||||
$client_id = intval($_POST['client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
|
||||
@@ -76,6 +76,7 @@ $billing_increment_minutes = (int) round($billing_increment_seconds / 60);
|
||||
|
||||
// Ticket-level billable flag (same as your original report)
|
||||
$billable_sql = $billable_only ? " AND t.ticket_billable = 1 " : "";
|
||||
$client_scope_sql = clientScopeSql('c.client_id');
|
||||
|
||||
/**
|
||||
* Query returns ONLY replies that have time_worked and are within date range.
|
||||
@@ -108,6 +109,7 @@ $stmt = $mysqli->prepare("
|
||||
|
||||
WHERE c.client_archived_at IS NULL
|
||||
$billable_sql
|
||||
$client_scope_sql
|
||||
|
||||
ORDER BY c.client_name ASC,
|
||||
t.ticket_number ASC,
|
||||
|
||||
@@ -48,9 +48,10 @@ $sql_payment_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(payment_date) A
|
||||
$sql_clients = "SELECT c.client_id, c.client_name, SUM(p.payment_amount) AS amount_paid
|
||||
FROM clients AS c
|
||||
JOIN invoices AS i ON c.client_id = i.invoice_client_id
|
||||
JOIN payments AS p ON i.invoice_id = p.payment_invoice_id";
|
||||
JOIN payments AS p ON i.invoice_id = p.payment_invoice_id
|
||||
WHERE 1=1" . clientScopeSql('c.client_id');
|
||||
if ($year != 'all') {
|
||||
$sql_clients .= " WHERE YEAR(p.payment_date) = $year";
|
||||
$sql_clients .= " AND YEAR(p.payment_date) = $year";
|
||||
}
|
||||
$sql_clients .= " GROUP BY c.client_id
|
||||
HAVING amount_paid > 599
|
||||
|
||||
@@ -38,6 +38,7 @@ enforceUserPermission('module_financial');
|
||||
GROUP BY payment_invoice_id) as payments
|
||||
ON
|
||||
invoices.invoice_id = payments.payment_invoice_id
|
||||
WHERE 1=1 " . clientScopeSql('clients.client_id') . "
|
||||
GROUP BY
|
||||
clients.client_id,
|
||||
clients.client_name
|
||||
|
||||
@@ -11,6 +11,7 @@ $sql = mysqli_query($mysqli, "
|
||||
FROM clients
|
||||
LEFT JOIN recurring_invoices ON client_id = recurring_invoice_client_id
|
||||
WHERE recurring_invoice_status = 1
|
||||
" . clientScopeSql('clients.client_id') . "
|
||||
GROUP BY clients.client_id
|
||||
HAVING recurring_monthly_total > 0
|
||||
ORDER BY recurring_monthly_total DESC
|
||||
|
||||
@@ -25,7 +25,7 @@ if ($month) {
|
||||
|
||||
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
|
||||
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL " . clientScopeSql('client_id') . " ORDER BY client_name ASC");
|
||||
|
||||
|
||||
// Clock time spent on resolved tickets, gathered for every client at once
|
||||
|
||||
@@ -18,7 +18,7 @@ if (isset($_GET['month'])) {
|
||||
|
||||
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
|
||||
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL " . clientScopeSql('client_id') . " ORDER BY client_name ASC");
|
||||
|
||||
?>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ if (isset($_GET['year'])) {
|
||||
|
||||
$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC");
|
||||
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients ORDER BY client_name ASC");
|
||||
$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE 1=1 " . clientScopeSql('client_id') . " ORDER BY client_name ASC");
|
||||
|
||||
$rows = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user