style: auto-fix 1340 phpcs PSR-12 violations via phpcbf; exclude MissingNamespace and SideEffects
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ApiKeyAuth - Handles API key authentication for external services
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/models/ApiKeyModel.php';
|
||||
require_once dirname(__DIR__) . '/models/UserModel.php';
|
||||
|
||||
class ApiKeyAuth {
|
||||
class ApiKeyAuth
|
||||
{
|
||||
private $apiKeyModel;
|
||||
private $userModel;
|
||||
private $conn;
|
||||
|
||||
public function __construct($conn) {
|
||||
public function __construct($conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->apiKeyModel = new ApiKeyModel($conn);
|
||||
$this->userModel = new UserModel($conn);
|
||||
@@ -22,7 +26,8 @@ class ApiKeyAuth {
|
||||
* @return array User data for system user
|
||||
* @throws Exception if authentication fails
|
||||
*/
|
||||
public function authenticate() {
|
||||
public function authenticate()
|
||||
{
|
||||
// Get Authorization header
|
||||
$authHeader = $this->getAuthorizationHeader();
|
||||
|
||||
@@ -67,7 +72,8 @@ class ApiKeyAuth {
|
||||
*
|
||||
* @return string|null Authorization header value
|
||||
*/
|
||||
private function getAuthorizationHeader() {
|
||||
private function getAuthorizationHeader()
|
||||
{
|
||||
// Try different header formats
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
return $_SERVER['HTTP_AUTHORIZATION'];
|
||||
@@ -96,7 +102,8 @@ class ApiKeyAuth {
|
||||
*
|
||||
* @param string $message Error message
|
||||
*/
|
||||
private function sendUnauthorized($message) {
|
||||
private function sendUnauthorized($message)
|
||||
{
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
@@ -111,7 +118,8 @@ class ApiKeyAuth {
|
||||
*
|
||||
* @return array|null User data or null if not authenticated
|
||||
*/
|
||||
public function verifyOptional() {
|
||||
public function verifyOptional()
|
||||
{
|
||||
$authHeader = $this->getAuthorizationHeader();
|
||||
|
||||
if (empty($authHeader)) {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AuthMiddleware - Handles authentication via Authelia forward auth headers
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/models/UserModel.php';
|
||||
|
||||
class AuthMiddleware {
|
||||
class AuthMiddleware
|
||||
{
|
||||
private $userModel;
|
||||
private $conn;
|
||||
|
||||
public function __construct($conn) {
|
||||
public function __construct($conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->userModel = new UserModel($conn);
|
||||
}
|
||||
@@ -19,7 +23,8 @@ class AuthMiddleware {
|
||||
* @param string $event Event type (e.g., 'auth_required', 'access_denied', 'session_expired')
|
||||
* @param array $context Additional context data
|
||||
*/
|
||||
private function logSecurityEvent(string $event, array $context = []): void {
|
||||
private function logSecurityEvent(string $event, array $context = []): void
|
||||
{
|
||||
$logData = [
|
||||
'event' => $event,
|
||||
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
||||
@@ -52,7 +57,8 @@ class AuthMiddleware {
|
||||
* @return array User data array
|
||||
* @throws Exception if authentication fails
|
||||
*/
|
||||
public function authenticate() {
|
||||
public function authenticate()
|
||||
{
|
||||
// Start session if not already started with secure settings
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
// Configure secure session settings
|
||||
@@ -136,7 +142,8 @@ class AuthMiddleware {
|
||||
* @param string $header Header name
|
||||
* @return string|null Header value or null if not set
|
||||
*/
|
||||
private function getHeader($header) {
|
||||
private function getHeader($header)
|
||||
{
|
||||
if (isset($_SERVER[$header])) {
|
||||
return $_SERVER[$header];
|
||||
}
|
||||
@@ -149,7 +156,8 @@ class AuthMiddleware {
|
||||
* @param string $groups Comma-separated group names
|
||||
* @return bool True if user has access
|
||||
*/
|
||||
private function checkGroupAccess($groups) {
|
||||
private function checkGroupAccess($groups)
|
||||
{
|
||||
if (empty($groups)) {
|
||||
return false;
|
||||
}
|
||||
@@ -158,7 +166,9 @@ class AuthMiddleware {
|
||||
// Filter to safe characters only to prevent header injection attacks
|
||||
$userGroups = array_filter(
|
||||
array_map('trim', explode(',', strtolower($groups))),
|
||||
function($g) { return preg_match('/^[a-z0-9_\-]+$/', $g); }
|
||||
function ($g) {
|
||||
return preg_match('/^[a-z0-9_\-]+$/', $g);
|
||||
}
|
||||
);
|
||||
$requiredGroups = ['admin', 'employee'];
|
||||
|
||||
@@ -168,7 +178,8 @@ class AuthMiddleware {
|
||||
/**
|
||||
* Redirect to Authelia login
|
||||
*/
|
||||
private function redirectToAuth() {
|
||||
private function redirectToAuth()
|
||||
{
|
||||
// Log unauthenticated access attempt
|
||||
$this->logSecurityEvent('auth_required', [
|
||||
'reason' => 'no_auth_headers'
|
||||
@@ -237,7 +248,8 @@ class AuthMiddleware {
|
||||
* @param string $username Username
|
||||
* @param string $groups User groups
|
||||
*/
|
||||
private function showAccessDenied($username, $groups) {
|
||||
private function showAccessDenied($username, $groups)
|
||||
{
|
||||
// Log access denied event with user details
|
||||
$this->logSecurityEvent('access_denied', [
|
||||
'username' => $username,
|
||||
@@ -308,7 +320,8 @@ class AuthMiddleware {
|
||||
*
|
||||
* @return array|null User data or null if not authenticated
|
||||
*/
|
||||
public static function getCurrentUser() {
|
||||
public static function getCurrentUser()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@@ -319,7 +332,8 @@ class AuthMiddleware {
|
||||
/**
|
||||
* Logout current user
|
||||
*/
|
||||
public static function logout() {
|
||||
public static function logout()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CSRF Protection Middleware
|
||||
* Generates and validates CSRF tokens for all state-changing operations
|
||||
*/
|
||||
class CsrfMiddleware {
|
||||
class CsrfMiddleware
|
||||
{
|
||||
private static string $tokenName = 'csrf_token';
|
||||
private static string $tokenTime = 'csrf_token_time';
|
||||
private static int $tokenLifetime = 3600; // 1 hour
|
||||
@@ -11,7 +13,8 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Generate a new CSRF token
|
||||
*/
|
||||
public static function generateToken(): string {
|
||||
public static function generateToken(): string
|
||||
{
|
||||
$_SESSION[self::$tokenName] = bin2hex(random_bytes(32));
|
||||
$_SESSION[self::$tokenTime] = time();
|
||||
return $_SESSION[self::$tokenName];
|
||||
@@ -20,7 +23,8 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Get current CSRF token, regenerate if expired
|
||||
*/
|
||||
public static function getToken(): string {
|
||||
public static function getToken(): string
|
||||
{
|
||||
if (!isset($_SESSION[self::$tokenName]) || self::isTokenExpired()) {
|
||||
return self::generateToken();
|
||||
}
|
||||
@@ -30,7 +34,8 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Validate CSRF token (constant-time comparison)
|
||||
*/
|
||||
public static function validateToken(string $token): bool {
|
||||
public static function validateToken(string $token): bool
|
||||
{
|
||||
if (!isset($_SESSION[self::$tokenName])) {
|
||||
return false;
|
||||
}
|
||||
@@ -52,14 +57,16 @@ class CsrfMiddleware {
|
||||
*
|
||||
* @return string The new token
|
||||
*/
|
||||
public static function rotateToken(): string {
|
||||
public static function rotateToken(): string
|
||||
{
|
||||
return self::generateToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if token is expired
|
||||
*/
|
||||
private static function isTokenExpired(): bool {
|
||||
private static function isTokenExpired(): bool
|
||||
{
|
||||
return !isset($_SESSION[self::$tokenTime]) ||
|
||||
(time() - $_SESSION[self::$tokenTime]) > self::$tokenLifetime;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Rate Limiting Middleware
|
||||
*
|
||||
* Implements both session-based and IP-based rate limiting to prevent abuse.
|
||||
* IP-based limiting prevents attackers from bypassing limits by creating new sessions.
|
||||
*/
|
||||
class RateLimitMiddleware {
|
||||
class RateLimitMiddleware
|
||||
{
|
||||
// Default limits
|
||||
public const DEFAULT_LIMIT = 100; // requests per window (session)
|
||||
public const API_LIMIT = 60; // API requests per window (session)
|
||||
@@ -21,7 +23,8 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @return string Path to rate limit storage directory
|
||||
*/
|
||||
private static function getRateLimitDir(): string {
|
||||
private static function getRateLimitDir(): string
|
||||
{
|
||||
if (self::$rateLimitDir === null) {
|
||||
self::$rateLimitDir = sys_get_temp_dir() . '/tinker_tickets_ratelimit';
|
||||
if (!is_dir(self::$rateLimitDir)) {
|
||||
@@ -36,7 +39,8 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @return string Client IP address
|
||||
*/
|
||||
private static function getClientIp(): string {
|
||||
private static function getClientIp(): string
|
||||
{
|
||||
// Check for forwarded IP (behind proxy/load balancer)
|
||||
$headers = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP'];
|
||||
foreach ($headers as $header) {
|
||||
@@ -58,7 +62,8 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return bool True if request is allowed, false if rate limited
|
||||
*/
|
||||
private static function checkIpRateLimit(string $type = 'default'): bool {
|
||||
private static function checkIpRateLimit(string $type = 'default'): bool
|
||||
{
|
||||
$ip = self::getClientIp();
|
||||
$limit = $type === 'api' ? self::IP_API_LIMIT : self::IP_LIMIT;
|
||||
$now = time();
|
||||
@@ -100,7 +105,8 @@ class RateLimitMiddleware {
|
||||
* Uses DirectoryIterator instead of glob() for better memory efficiency.
|
||||
* A dedicated cron script (cron/cleanup_ratelimit.php) should also run for reliable cleanup.
|
||||
*/
|
||||
public static function cleanupOldFiles(): void {
|
||||
public static function cleanupOldFiles(): void
|
||||
{
|
||||
$dir = self::getRateLimitDir();
|
||||
$lockFile = $dir . '/.cleanup.lock';
|
||||
$now = time();
|
||||
@@ -157,7 +163,8 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return bool True if request is allowed, false if rate limited
|
||||
*/
|
||||
public static function check(string $type = 'default'): bool {
|
||||
public static function check(string $type = 'default'): bool
|
||||
{
|
||||
// First check IP-based rate limit (prevents session bypass)
|
||||
if (!self::checkIpRateLimit($type)) {
|
||||
return false;
|
||||
@@ -206,7 +213,8 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @param bool $addHeaders Whether to add rate limit headers to response
|
||||
*/
|
||||
public static function apply(string $type = 'default', bool $addHeaders = true): void {
|
||||
public static function apply(string $type = 'default', bool $addHeaders = true): void
|
||||
{
|
||||
// Periodically clean up old rate limit files (2% chance per request)
|
||||
// Note: For production, use cron/cleanup_ratelimit.php for reliable cleanup
|
||||
if (mt_rand(1, 50) === 1) {
|
||||
@@ -240,7 +248,8 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return array Rate limit status
|
||||
*/
|
||||
public static function getStatus(string $type = 'default'): array {
|
||||
public static function getStatus(string $type = 'default'): array
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@@ -280,7 +289,8 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @param string $type 'default' or 'api'
|
||||
*/
|
||||
public static function addHeaders(string $type = 'default'): void {
|
||||
public static function addHeaders(string $type = 'default'): void
|
||||
{
|
||||
$status = self::getStatus($type);
|
||||
header('X-RateLimit-Limit: ' . $status['limit']);
|
||||
header('X-RateLimit-Remaining: ' . $status['remaining']);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Security Headers Middleware
|
||||
*
|
||||
* Applies security-related HTTP headers to all responses.
|
||||
*/
|
||||
class SecurityHeadersMiddleware {
|
||||
class SecurityHeadersMiddleware
|
||||
{
|
||||
private static ?string $nonce = null;
|
||||
|
||||
/**
|
||||
@@ -12,7 +14,8 @@ class SecurityHeadersMiddleware {
|
||||
*
|
||||
* @return string The nonce value
|
||||
*/
|
||||
public static function getNonce(): string {
|
||||
public static function getNonce(): string
|
||||
{
|
||||
if (self::$nonce === null) {
|
||||
self::$nonce = base64_encode(random_bytes(16));
|
||||
}
|
||||
@@ -22,7 +25,8 @@ class SecurityHeadersMiddleware {
|
||||
/**
|
||||
* Apply security headers to the response
|
||||
*/
|
||||
public static function apply(): void {
|
||||
public static function apply(): void
|
||||
{
|
||||
$nonce = self::getNonce();
|
||||
|
||||
// Content Security Policy - restricts where resources can be loaded from
|
||||
|
||||
Reference in New Issue
Block a user