Repository navigation
Expand file tree
/
Copy pathconfig.php
More file actions
209 lines (184 loc) · 7.14 KB
/
Copy pathconfig.php
File metadata and controls
209 lines (184 loc) · 7.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
<?php
/**
* Database Configuration
*/
if (PHP_SAPI === 'cli') {
define('BASE_URL', '/expenses/');
} else {
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
define('BASE_URL', (strpos($scriptName, '/expenses/') === 0) ? '/expenses/' : '/');
}
// Load .env variables
$envFile = __DIR__ . '/.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || $line[0] === '#') {
continue;
}
$parts = explode('=', $line, 2);
if (count($parts) !== 2) {
continue; // skip malformed
}
$name = trim($parts[0]);
$value = trim($parts[1]);
if ($value !== '' && ($value[0] === '"' || $value[0] === "'")) {
$value = trim($value, "\"'");
} else {
// Unquoted values may carry an inline comment: KEY=value # comment
$value = trim(preg_replace('/\s+#.*$/', '', $value));
}
if ($name === '') {
continue;
}
putenv(sprintf('%s=%s', $name, $value));
$_ENV[$name] = $value;
}
}
// Force HTTPS in production
if (($_ENV['APP_ENV'] ?? 'production') === 'production'
&& PHP_SAPI !== 'cli'
&& empty($_SERVER['HTTPS'])
&& ($_SERVER['SERVER_PORT'] ?? 80) != 443) {
$host = $_SERVER['HTTP_HOST'] ?? '';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
header('Location: https://' . $host . $uri, true, 301);
exit();
}
// Set Timezone
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Asia/Dubai');
// Database Host (e.g., localhost or IP address)
define('DB_HOST', $_ENV['DB_HOST'] ?? 'localhost');
// Database Name
define('DB_NAME', $_ENV['DB_NAME'] ?? '');
// Database Username
define('DB_USER', $_ENV['DB_USER'] ?? '');
// Database Password
define('DB_PASS', $_ENV['DB_PASS'] ?? '');
// Charset
define('DB_CHARSET', 'utf8mb4');
try {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
// Sync DB timezone with PHP timezone
$now = new DateTime();
$mins = $now->getOffset() / 60;
$sgn = ($mins < 0 ? -1 : 1);
$mins = abs($mins);
$hrs = floor($mins / 60);
$mins -= $hrs * 60;
$offset = sprintf('%+d:%02d', $hrs * $sgn, $mins);
$pdo->prepare("SET time_zone = ?")->execute([$offset]);
} catch (\PDOException $e) {
// Production Error Handling
error_log("Database Connection Error: " . $e->getMessage()); // Log to server error log
die("Global Finance Error: Service temporarily unavailable. Please try again later.");
}
// Production Settings
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL);
// Secure Error Logging
// The parent folder is NOT safe: on this host it is the main site's public_html, where the
// log was publicly downloadable. Log into logs/, which is denied by logs/.htaccess and the
// app's root .htaccess.
$logDir = __DIR__ . '/logs';
if (!is_dir($logDir)) {
@mkdir($logDir, 0750, true);
}
$logFile = $logDir . '/php_errors.log';
if (!is_writable($logDir) && !is_writable($logFile)) {
$logFile = __DIR__ . '/.error.log'; // *.log is denied by the root .htaccess
}
ini_set('log_errors', 1);
ini_set('error_log', $logFile);
// Global exception handler — logs the error, shows a generic message to the user
set_exception_handler(function (Throwable $e): void {
error_log('[Uncaught] ' . get_class($e) . ': ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
if (PHP_SAPI !== 'cli' && !headers_sent()) {
http_response_code(500);
}
echo 'An unexpected error occurred. Please try again later.';
exit(1);
});
// CSRF Protection & Session Hardening
if (session_status() === PHP_SESSION_NONE) {
// secure session cookie params
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => $secure,
'httponly' => true,
'samesite' => 'Strict'
]);
session_start();
}
// Server-side session idle timeout (1 hour)
if (isset($_SESSION['user_id'])) {
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > 3600) {
$_SESSION = [];
if (PHP_SAPI !== 'cli' && !headers_sent()) {
session_regenerate_id(true);
$_SESSION['_flash'][] = ['type' => 'warning', 'message' => 'Your session expired. Please sign in again.'];
header('Location: ' . BASE_URL . 'index.php');
exit();
}
} else {
$_SESSION['last_activity'] = time();
}
}
// Keep the session in sync with the account: role/permission changes made by an admin
// apply immediately, and a password change, reset or revoke elsewhere signs this session out.
if (isset($_SESSION['user_id'])) {
try {
$acctStmt = $pdo->prepare("SELECT name, role, permission, tenant_id, password FROM users WHERE id = ?");
$acctStmt->execute([$_SESSION['user_id']]);
$acct = $acctStmt->fetch();
$pwFingerprint = $acct ? hash('sha256', $acct['password']) : '';
if (!$acct || (isset($_SESSION['pw_fp']) && !hash_equals($_SESSION['pw_fp'], $pwFingerprint))) {
$_SESSION = [];
if (PHP_SAPI !== 'cli' && !headers_sent()) {
session_regenerate_id(true);
$_SESSION['_flash'][] = ['type' => 'warning', 'message' => 'Your password was changed. Please sign in again.'];
header('Location: ' . BASE_URL . 'index.php');
exit();
}
} else {
$_SESSION['pw_fp'] = $pwFingerprint;
$_SESSION['user_name'] = $acct['name'];
$_SESSION['role'] = $acct['role'];
$_SESSION['permission'] = $acct['permission'];
$_SESSION['tenant_id'] = $acct['tenant_id'];
}
unset($acctStmt, $acct, $pwFingerprint);
} catch (\PDOException $e) {
error_log("Session account check failed: " . $e->getMessage());
}
}
// Load Composer Autoloader
require_once __DIR__ . '/autoload.php';
// procedural version removed in favor of App\Helpers\AuditHelper
// Load User Preferences into Session
if (isset($_SESSION['user_id']) && !isset($_SESSION['preferences'])) {
try {
$prefStmt = $pdo->prepare("SELECT * FROM user_preferences WHERE user_id = ?");
$prefStmt->execute([$_SESSION['user_id']]);
$prefs = $prefStmt->fetch();
if (!$prefs) {
// Create default
$pdo->prepare("INSERT INTO user_preferences (user_id) VALUES (?)")->execute([$_SESSION['user_id']]);
$prefs = ['base_currency' => 'AED', 'theme_preference' => 'dark', 'notifications_enabled' => 1];
}
$_SESSION['preferences'] = $prefs;
} catch (Exception $e) {
$_SESSION['preferences'] = ['base_currency' => 'AED', 'theme_preference' => 'dark'];
}
}