Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property types across all core classes: - helpers: Database, ErrorHandler, CacheHelper - models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel - middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
* Generates and validates CSRF tokens for all state-changing operations
|
||||
*/
|
||||
class CsrfMiddleware {
|
||||
private static $tokenName = 'csrf_token';
|
||||
private static $tokenTime = 'csrf_token_time';
|
||||
private static $tokenLifetime = 3600; // 1 hour
|
||||
private static string $tokenName = 'csrf_token';
|
||||
private static string $tokenTime = 'csrf_token_time';
|
||||
private static int $tokenLifetime = 3600; // 1 hour
|
||||
|
||||
/**
|
||||
* Generate a new CSRF token
|
||||
*/
|
||||
public static function generateToken() {
|
||||
public static function generateToken(): string {
|
||||
$_SESSION[self::$tokenName] = bin2hex(random_bytes(32));
|
||||
$_SESSION[self::$tokenTime] = time();
|
||||
return $_SESSION[self::$tokenName];
|
||||
@@ -20,7 +20,7 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Get current CSRF token, regenerate if expired
|
||||
*/
|
||||
public static function getToken() {
|
||||
public static function getToken(): string {
|
||||
if (!isset($_SESSION[self::$tokenName]) || self::isTokenExpired()) {
|
||||
return self::generateToken();
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Validate CSRF token (constant-time comparison)
|
||||
*/
|
||||
public static function validateToken($token) {
|
||||
public static function validateToken(string $token): bool {
|
||||
if (!isset($_SESSION[self::$tokenName])) {
|
||||
return false;
|
||||
}
|
||||
@@ -47,9 +47,8 @@ class CsrfMiddleware {
|
||||
/**
|
||||
* Check if token is expired
|
||||
*/
|
||||
private static function isTokenExpired() {
|
||||
private static function isTokenExpired(): bool {
|
||||
return !isset($_SESSION[self::$tokenTime]) ||
|
||||
(time() - $_SESSION[self::$tokenTime]) > self::$tokenLifetime;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
*/
|
||||
class RateLimitMiddleware {
|
||||
// Default limits
|
||||
const DEFAULT_LIMIT = 100; // requests per window (session)
|
||||
const API_LIMIT = 60; // API requests per window (session)
|
||||
const IP_LIMIT = 300; // IP-based requests per window (more generous)
|
||||
const IP_API_LIMIT = 120; // IP-based API requests per window
|
||||
const WINDOW_SECONDS = 60; // 1 minute window
|
||||
public const DEFAULT_LIMIT = 100; // requests per window (session)
|
||||
public const API_LIMIT = 60; // API requests per window (session)
|
||||
public const IP_LIMIT = 300; // IP-based requests per window (more generous)
|
||||
public const IP_API_LIMIT = 120; // IP-based API requests per window
|
||||
public const WINDOW_SECONDS = 60; // 1 minute window
|
||||
|
||||
// Directory for IP rate limit storage
|
||||
private static $rateLimitDir = null;
|
||||
private static ?string $rateLimitDir = null;
|
||||
|
||||
/**
|
||||
* Get the rate limit storage directory
|
||||
*
|
||||
* @return string Path to rate limit storage directory
|
||||
*/
|
||||
private static function getRateLimitDir() {
|
||||
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 +36,7 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @return string Client IP address
|
||||
*/
|
||||
private static function getClientIp() {
|
||||
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 +58,7 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return bool True if request is allowed, false if rate limited
|
||||
*/
|
||||
private static function checkIpRateLimit($type = 'default') {
|
||||
private static function checkIpRateLimit(string $type = 'default'): bool {
|
||||
$ip = self::getClientIp();
|
||||
$limit = $type === 'api' ? self::IP_API_LIMIT : self::IP_LIMIT;
|
||||
$now = time();
|
||||
@@ -97,7 +97,7 @@ class RateLimitMiddleware {
|
||||
/**
|
||||
* Clean up old rate limit files (call periodically)
|
||||
*/
|
||||
public static function cleanupOldFiles() {
|
||||
public static function cleanupOldFiles(): void {
|
||||
$dir = self::getRateLimitDir();
|
||||
$files = glob($dir . '/*.json');
|
||||
$now = time();
|
||||
@@ -116,7 +116,7 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return bool True if request is allowed, false if rate limited
|
||||
*/
|
||||
public static function check($type = 'default') {
|
||||
public static function check(string $type = 'default'): bool {
|
||||
// First check IP-based rate limit (prevents session bypass)
|
||||
if (!self::checkIpRateLimit($type)) {
|
||||
return false;
|
||||
@@ -164,7 +164,7 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @param string $type 'default' or 'api'
|
||||
*/
|
||||
public static function apply($type = 'default') {
|
||||
public static function apply(string $type = 'default'): void {
|
||||
// Periodically clean up old rate limit files (1% chance per request)
|
||||
if (mt_rand(1, 100) === 1) {
|
||||
self::cleanupOldFiles();
|
||||
@@ -189,7 +189,7 @@ class RateLimitMiddleware {
|
||||
* @param string $type 'default' or 'api'
|
||||
* @return array Rate limit status
|
||||
*/
|
||||
public static function getStatus($type = 'default') {
|
||||
public static function getStatus(string $type = 'default'): array {
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@@ -229,7 +229,7 @@ class RateLimitMiddleware {
|
||||
*
|
||||
* @param string $type 'default' or 'api'
|
||||
*/
|
||||
public static function addHeaders($type = 'default') {
|
||||
public static function addHeaders(string $type = 'default'): void {
|
||||
$status = self::getStatus($type);
|
||||
header('X-RateLimit-Limit: ' . $status['limit']);
|
||||
header('X-RateLimit-Remaining: ' . $status['remaining']);
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
* Applies security-related HTTP headers to all responses.
|
||||
*/
|
||||
class SecurityHeadersMiddleware {
|
||||
private static $nonce = null;
|
||||
private static ?string $nonce = null;
|
||||
|
||||
/**
|
||||
* Generate or retrieve the CSP nonce for this request
|
||||
*
|
||||
* @return string The nonce value
|
||||
*/
|
||||
public static function getNonce() {
|
||||
public static function getNonce(): string {
|
||||
if (self::$nonce === null) {
|
||||
self::$nonce = base64_encode(random_bytes(16));
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class SecurityHeadersMiddleware {
|
||||
/**
|
||||
* Apply security headers to the response
|
||||
*/
|
||||
public static function apply() {
|
||||
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