Enforce roles on admin endpoints and return real status codes
- users, SMTP and batch-lock endpoints are owner/admin only - engines answer 400/403/404/409/500 instead of 200 with an error body; database errors no longer leak to the client
This commit is contained in:
@@ -3,6 +3,9 @@ require_once __DIR__ . '/../../../session.php';
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes/OperationLockManager.php';
|
||||
|
||||
// Batch GL posting takes this lock; posting itself is owner/admin only.
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
try {
|
||||
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
||||
$result = $lock_manager->acquire(
|
||||
@@ -10,7 +13,15 @@ try {
|
||||
(int)($data['ttl_minutes'] ?? 120)
|
||||
);
|
||||
$answer = array_merge($answer, $result);
|
||||
if (empty($result['success'])) {
|
||||
http_response_code(409); // another tab or user holds the lock
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log('[acquire_op_lock] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ $doc_type = trim((string)($data['doc_type'] ?? ''));
|
||||
$source_id = (int)($data['source_id'] ?? 0);
|
||||
|
||||
if (!$doc_type || $source_id <= 0) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'doc_type and source_id required.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -27,8 +28,13 @@ try {
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['output'] = $detail;
|
||||
} catch (PDOException $e) {
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -12,8 +12,13 @@ try {
|
||||
(int)($data['formula_id'] ?? 0)
|
||||
);
|
||||
$answer['success'] = 1;
|
||||
} catch (PDOException $e) {
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -12,6 +12,7 @@ $to_date = trim((string)($data['to_date'] ?? ($data['to_period'] ??
|
||||
$dept_id = (int)($data['department_id'] ?? 0);
|
||||
|
||||
if ($account_code === '') {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'account_code is required.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@ try {
|
||||
$gl_query = new GlQueryManager($pdo2, $company_id);
|
||||
$answer['output'] = $gl_query->getJournalDetail((int)($data['gl_id'] ?? 0));
|
||||
$answer['success'] = 1;
|
||||
} catch (PDOException $e) {
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -11,8 +11,13 @@ try {
|
||||
trim((string)($data['date_to'] ?? ''))
|
||||
);
|
||||
$answer['success'] = 1;
|
||||
} catch (PDOException $e) {
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php';
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes/BatchActionManager.php';
|
||||
|
||||
// Logged at the end of a batch GL posting run, which is owner/admin only.
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
try {
|
||||
$batch_action = new BatchActionManager($pdo2, $company_id, $user_id);
|
||||
$batch_action->log($data);
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Batch action logged.';
|
||||
} catch (PDOException $e) {
|
||||
error_log('[log_batch_action] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
if (empty($data['account_code']) || empty($data['account_name']) || empty($data['account_type'])) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Account code, name, and type are required';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
if (empty($data['dept_code']) || empty($data['dept_name'])) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Department code and name are required';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ $posting_map = [
|
||||
];
|
||||
|
||||
if (!isset($posting_map[$doc_type]) || $id <= 0) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Invalid doc_type or id.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -73,9 +74,15 @@ try {
|
||||
'has_expense' => $has_expense,
|
||||
], $company_id);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -30,14 +30,17 @@ if ($data['action'] === 'save') {
|
||||
$to = trim((string)($data['open_to'] ?? ''));
|
||||
|
||||
if ($from !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $from)) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Invalid open_from date. Use YYYY-MM-DD.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
if ($to !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $to)) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Invalid open_to date. Use YYYY-MM-DD.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
if ($from && $to && $from > $to) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Open From must be on or before Open To.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -50,5 +53,6 @@ if ($data['action'] === 'save') {
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Invalid action.';
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -3,12 +3,20 @@ require_once __DIR__ . '/../../../session.php';
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes/OperationLockManager.php';
|
||||
|
||||
// Batch GL posting takes this lock; posting itself is owner/admin only.
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
try {
|
||||
$lock_manager = new OperationLockManager($pdo2, $company_id, $user_id);
|
||||
$lock_manager->release(trim((string)($data['operation_type'] ?? '')));
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Lock released.';
|
||||
} catch (PDOException $e) {
|
||||
error_log('[release_op_lock] ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
} catch (Exception $e) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Missing id';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) { $answer['message'] = 'Missing id'; exit(json_encode($answer)); }
|
||||
if (!$id) { http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); }
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$dept->delete($id);
|
||||
|
||||
@@ -5,6 +5,7 @@ require_once '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Missing id';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -12,6 +13,7 @@ if (!$id) {
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$row = $coa->getById($id);
|
||||
if (!$row) {
|
||||
http_response_code(404);
|
||||
$answer['message'] = 'Account not found';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) { $answer['message'] = 'Missing id'; exit(json_encode($answer)); }
|
||||
if (!$id) { http_response_code(400); $answer['message'] = 'Missing id'; exit(json_encode($answer)); }
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$row = $dept->getById($id);
|
||||
if (!$row) { $answer['message'] = 'Department not found'; exit(json_encode($answer)); }
|
||||
if (!$row) { http_response_code(404); $answer['message'] = 'Department not found'; exit(json_encode($answer)); }
|
||||
|
||||
$answer['output'] = $row;
|
||||
$answer['success'] = 1;
|
||||
|
||||
@@ -25,6 +25,7 @@ if (preg_match('#^(\d{2})/(\d{2})/(\d{4})$#', $journal_date, $m)) {
|
||||
}
|
||||
|
||||
if (!$journal_date || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $journal_date)) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Valid journal date is required.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -51,11 +52,13 @@ foreach ($lines_raw as $l) {
|
||||
}
|
||||
|
||||
if (count($lines) < 2) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'At least two journal lines are required.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
if (abs($total_debit - $total_credit) > 0.005) {
|
||||
http_response_code(400);
|
||||
$answer['message'] = 'Journal is not balanced. Debit ' . number_format($total_debit, 2) . ' ≠ Credit ' . number_format($total_credit, 2) . '.';
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
@@ -83,9 +86,15 @@ try {
|
||||
$answer['success'] = 1;
|
||||
$answer['gl_id'] = $gl_id;
|
||||
notify_node('gl_posted', gl_posted_payload('manual', (int)$gl_id, $event_action, $lines), $company_id);
|
||||
} catch (PDOException $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
error_log('[' . basename(__FILE__) . '] ' . $e->getMessage());
|
||||
$answer['message'] = 'Database error, please try again.';
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo2->inTransaction()) $pdo2->rollBack();
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
|
||||
Reference in New Issue
Block a user