- Route index.php and create_ticket_api.php through Database::getConnection() (#103, #104) - Make TRUSTED_PROXIES' insecure-by-default risk loudly visible (#94) - Add recovery csrf_token to 12 hand-rolled CSRF rejection responses (#85)
This commit is contained in:
+11
-4
@@ -49,19 +49,26 @@ APP_DOMAIN=
|
|||||||
; Include all domains that can access this application
|
; Include all domains that can access this application
|
||||||
ALLOWED_HOSTS=localhost,127.0.0.1
|
ALLOWED_HOSTS=localhost,127.0.0.1
|
||||||
|
|
||||||
|
; ============================================================================
|
||||||
|
; REQUIRED FOR PRODUCTION -- READ BEFORE DEPLOYING -- TRUSTED_PROXIES
|
||||||
|
; ============================================================================
|
||||||
; Trusted reverse proxy IPs, comma-separated -- e.g. the Authelia/nginx proxy.
|
; Trusted reverse proxy IPs, comma-separated -- e.g. the Authelia/nginx proxy.
|
||||||
; Set this to the IP address(es) of your reverse proxy. Authelia forward-auth
|
; Set this to the IP address(es) of your reverse proxy. Authelia forward-auth
|
||||||
; headers (Remote-User / Remote-Groups) and forwarded client IPs are only
|
; headers (Remote-User / Remote-Groups) and forwarded client IPs are only
|
||||||
; trusted when REMOTE_ADDR is in this list.
|
; trusted when REMOTE_ADDR is in this list.
|
||||||
;
|
;
|
||||||
; Leaving this EMPTY disables reverse-proxy verification entirely: the app then
|
; Leaving this EMPTY disables reverse-proxy verification entirely: the app then
|
||||||
; trusts Remote-User / Remote-Groups headers from ANY source. That is unsafe if
|
; trusts Remote-User / Remote-Groups headers from ANY source. If the PHP
|
||||||
; the PHP backend is reachable directly (bypassing the proxy), because a client
|
; backend is reachable directly -- a misconfigured firewall rule, a container
|
||||||
; can then spoof those headers and log in as an admin. Only leave it empty when
|
; network accidentally exposing the port, SSRF from another internal service
|
||||||
; network topology guarantees PHP is reachable solely via the trusted proxy.
|
; -- ANYONE can set Remote-User: admin themselves and fully impersonate any
|
||||||
|
; user, including an admin, with ZERO authentication. Only leave it empty when
|
||||||
|
; network topology guarantees PHP is reachable solely via the trusted proxy
|
||||||
|
; (e.g. local development), never in a real deployment.
|
||||||
;
|
;
|
||||||
; Exact IP match only (no CIDR). Example (single proxy): TRUSTED_PROXIES=10.10.10.27
|
; Exact IP match only (no CIDR). Example (single proxy): TRUSTED_PROXIES=10.10.10.27
|
||||||
; Example (multiple): TRUSTED_PROXIES=10.10.10.27,10.10.10.28
|
; Example (multiple): TRUSTED_PROXIES=10.10.10.27,10.10.10.28
|
||||||
|
; ============================================================================
|
||||||
TRUSTED_PROXIES=
|
TRUSTED_PROXIES=
|
||||||
|
|
||||||
; Timezone (default: America/New_York)
|
; Timezone (default: America/New_York)
|
||||||
|
|||||||
@@ -447,6 +447,21 @@ APP_DOMAIN=your.domain.example
|
|||||||
TIMEZONE=America/New_York
|
TIMEZONE=America/New_York
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**⚠️ REQUIRED FOR PRODUCTION — `TRUSTED_PROXIES`:** This app trusts Authelia
|
||||||
|
forward-auth headers (`Remote-User`, `Remote-Groups`, etc.) to identify who's
|
||||||
|
logged in. `TRUSTED_PROXIES` restricts that trust to requests that actually
|
||||||
|
came through your reverse proxy — **leaving it empty disables that check
|
||||||
|
entirely**, and anyone who can reach the PHP backend directly (a
|
||||||
|
misconfigured firewall rule, an exposed container port, SSRF from another
|
||||||
|
internal service) can set `Remote-User: admin` themselves and fully
|
||||||
|
impersonate any user with zero authentication. Set it to your reverse proxy's
|
||||||
|
IP address(es) before deploying anywhere reachable beyond your own machine:
|
||||||
|
```env
|
||||||
|
TRUSTED_PROXIES=10.10.10.27
|
||||||
|
```
|
||||||
|
`GET /api/health.php` reports a `warning` on the `trusted_proxies` check if
|
||||||
|
this is left empty, so it doesn't go unnoticed after deployment.
|
||||||
|
|
||||||
Matrix notification variables (all optional):
|
Matrix notification variables (all optional):
|
||||||
```env
|
```env
|
||||||
# hookshot generic webhook URL — send events to Matrix room
|
# hookshot generic webhook URL — send events to Matrix room
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ if (!in_array($_SERVER['REQUEST_METHOD'], ['GET', 'HEAD'], true)) {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ try {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ try {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
// Verify CSRF token
|
// Verify CSRF token
|
||||||
$csrfToken = $input['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $input['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
ResponseHelper::forbidden('Invalid CSRF token');
|
ResponseHelper::error('Invalid CSRF token', 403, ['csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get attachment ID
|
// Get attachment ID
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ try {
|
|||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,15 @@ try {
|
|||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
|
ob_end_clean();
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
throw new Exception("Invalid CSRF token");
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Invalid CSRF token',
|
||||||
|
'csrf_token' => CsrfMiddleware::getToken()
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,21 @@ if ($maxExecTime === 0 || $maxExecTime >= $requirements['min_max_execution_time'
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check 8: TRUSTED_PROXIES configured. Empty disables enforceTrustedProxy()'s
|
||||||
|
// allowlist entirely, meaning anything that can reach this app directly can
|
||||||
|
// spoof the Authelia forward-auth Remote-* headers and impersonate any user,
|
||||||
|
// including an admin. Not fatal (a fresh/dev install may not sit behind a
|
||||||
|
// proxy yet), but should never go unnoticed on a real deployment.
|
||||||
|
if (!empty($GLOBALS['config']['TRUSTED_PROXIES'] ?? [])) {
|
||||||
|
$checks['trusted_proxies'] = ['status' => 'ok', 'message' => 'configured'];
|
||||||
|
} else {
|
||||||
|
$checks['trusted_proxies'] = [
|
||||||
|
'status' => 'warning',
|
||||||
|
'message' => 'TRUSTED_PROXIES is empty — forward-auth headers are NOT verified; '
|
||||||
|
. 'anything that can reach this app directly can impersonate any user'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate response time
|
// Calculate response time
|
||||||
$responseTime = round((microtime(true) - $startTime) * 1000, 2);
|
$responseTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ try {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ try {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ try {
|
|||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']);
|
echo json_encode(['success' => false, 'error' => 'Invalid CSRF token', 'csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,15 @@ try {
|
|||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
|
ob_end_clean();
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
throw new Exception("Invalid CSRF token");
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Invalid CSRF token',
|
||||||
|
'csrf_token' => CsrfMiddleware::getToken()
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'DEL
|
|||||||
require_once dirname(__DIR__) . '/middleware/CsrfMiddleware.php';
|
require_once dirname(__DIR__) . '/middleware/CsrfMiddleware.php';
|
||||||
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
ResponseHelper::forbidden('Invalid CSRF token');
|
ResponseHelper::error('Invalid CSRF token', 403, ['csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|||||||
// Verify CSRF token
|
// Verify CSRF token
|
||||||
$csrfToken = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
$csrfToken = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
|
||||||
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
if (!CsrfMiddleware::validateToken($csrfToken)) {
|
||||||
ResponseHelper::forbidden('Invalid CSRF token');
|
ResponseHelper::error('Invalid CSRF token', 403, ['csrf_token' => CsrfMiddleware::getToken()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ticket ID
|
// Get ticket ID
|
||||||
|
|||||||
+17
-39
@@ -8,9 +8,10 @@ ini_set('display_errors', 0);
|
|||||||
require_once __DIR__ . '/middleware/RateLimitMiddleware.php';
|
require_once __DIR__ . '/middleware/RateLimitMiddleware.php';
|
||||||
RateLimitMiddleware::apply('api');
|
RateLimitMiddleware::apply('api');
|
||||||
|
|
||||||
// Load environment variables with error check
|
// Early friendly JSON error if .env is missing, before config.php's own
|
||||||
$envFile = __DIR__ . '/.env';
|
// (plain-text die()) handling would otherwise run — this is a JSON API
|
||||||
if (!file_exists($envFile)) {
|
// endpoint and must always respond with a JSON body.
|
||||||
|
if (!file_exists(__DIR__ . '/.env')) {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Configuration file not found'
|
'error' => 'Configuration file not found'
|
||||||
@@ -18,37 +19,17 @@ if (!file_exists($envFile)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$envVars = parse_ini_file($envFile, false, INI_SCANNER_TYPED);
|
// Load application config so UrlHelper can resolve APP_DOMAIN, and so the
|
||||||
if (!$envVars) {
|
// DB connection below (via Database::getConnection()) gets the same
|
||||||
echo json_encode([
|
// charset/timezone sync as every other endpoint instead of a hand-rolled
|
||||||
'success' => false,
|
// second connection.
|
||||||
'error' => 'Invalid configuration file'
|
require_once __DIR__ . '/config/config.php';
|
||||||
]);
|
require_once __DIR__ . '/helpers/Database.php';
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip quotes from values if present (parse_ini_file may include them)
|
try {
|
||||||
foreach ($envVars as $key => $value) {
|
$conn = Database::getConnection();
|
||||||
if (is_string($value)) {
|
} catch (\Throwable $e) {
|
||||||
if (
|
error_log('create_ticket_api: DB connection failed: ' . $e->getMessage());
|
||||||
(substr($value, 0, 1) === '"' && substr($value, -1) === '"') ||
|
|
||||||
(substr($value, 0, 1) === "'" && substr($value, -1) === "'")
|
|
||||||
) {
|
|
||||||
$envVars[$key] = substr($value, 1, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database connection with detailed error handling
|
|
||||||
$conn = new mysqli(
|
|
||||||
$envVars['DB_HOST'],
|
|
||||||
$envVars['DB_USER'],
|
|
||||||
$envVars['DB_PASS'],
|
|
||||||
$envVars['DB_NAME']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
error_log('create_ticket_api: DB connection failed: ' . $conn->connect_error);
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -57,9 +38,6 @@ if ($conn->connect_error) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load application config so UrlHelper can resolve APP_DOMAIN
|
|
||||||
require_once __DIR__ . '/config/config.php';
|
|
||||||
|
|
||||||
// Authenticate via API key
|
// Authenticate via API key
|
||||||
require_once __DIR__ . '/middleware/ApiKeyAuth.php';
|
require_once __DIR__ . '/middleware/ApiKeyAuth.php';
|
||||||
require_once __DIR__ . '/models/AuditLogModel.php';
|
require_once __DIR__ . '/models/AuditLogModel.php';
|
||||||
@@ -349,7 +327,7 @@ if ($existing) {
|
|||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'ticket_id' => $existingId,
|
'ticket_id' => $existingId,
|
||||||
@@ -386,7 +364,7 @@ if ($existing) {
|
|||||||
// Ticket reopened (Closed → Open) — refresh dashboard stats.
|
// Ticket reopened (Closed → Open) — refresh dashboard stats.
|
||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
|
|
||||||
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
||||||
NotificationHelper::sendTicketNotification($existingId, [
|
NotificationHelper::sendTicketNotification($existingId, [
|
||||||
@@ -484,7 +462,7 @@ if ($inserted) {
|
|||||||
// New ticket created — refresh dashboard stats.
|
// New ticket created — refresh dashboard stats.
|
||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
|
|
||||||
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
||||||
NotificationHelper::sendTicketNotification($ticket_id, [
|
NotificationHelper::sendTicketNotification($ticket_id, [
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ require_once 'config/config.php';
|
|||||||
require_once 'middleware/SecurityHeadersMiddleware.php';
|
require_once 'middleware/SecurityHeadersMiddleware.php';
|
||||||
require_once 'middleware/AuthMiddleware.php';
|
require_once 'middleware/AuthMiddleware.php';
|
||||||
require_once 'models/AuditLogModel.php';
|
require_once 'models/AuditLogModel.php';
|
||||||
|
require_once 'helpers/Database.php';
|
||||||
|
|
||||||
// Apply security headers early
|
// Apply security headers early
|
||||||
SecurityHeadersMiddleware::apply();
|
SecurityHeadersMiddleware::apply();
|
||||||
@@ -17,15 +18,12 @@ $requestPath = strtok($request, '?');
|
|||||||
|
|
||||||
// Create database connection for non-API routes
|
// Create database connection for non-API routes
|
||||||
if (!str_starts_with($requestPath, '/api/')) {
|
if (!str_starts_with($requestPath, '/api/')) {
|
||||||
$conn = new mysqli(
|
try {
|
||||||
$GLOBALS['config']['DB_HOST'],
|
$conn = Database::getConnection();
|
||||||
$GLOBALS['config']['DB_USER'],
|
} catch (\Throwable $e) {
|
||||||
$GLOBALS['config']['DB_PASS'],
|
error_log('index.php: database connection failed: ' . $e->getMessage());
|
||||||
$GLOBALS['config']['DB_NAME']
|
http_response_code(500);
|
||||||
);
|
die('Sorry, something went wrong. Please try again shortly.');
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Connection failed: " . $conn->connect_error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate user via Authelia forward auth
|
// Authenticate user via Authelia forward auth
|
||||||
@@ -444,5 +442,5 @@ switch (true) {
|
|||||||
|
|
||||||
// Close database connection if it was opened
|
// Close database connection if it was opened
|
||||||
if (isset($conn)) {
|
if (isset($conn)) {
|
||||||
$conn->close();
|
Database::close();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user