user(); if (!$user) { return false; } // Allow if Developer or legacy manage permission exists if ($user->hasRole('Developer') || $user->can('manage global settings')) { return true; } // Allow if any granular manage global settings permission exists (direct or via role) return $user->getAllPermissions() ->contains(fn ($p) => str_starts_with($p->name, 'manage global settings:')); } public function rules(): array { return [ 'app_name' => ['sometimes', 'required', 'string', 'min:3', 'max:100'], 'app_tagline' => ['nullable', 'string', 'max:150'], 'app_tagline1' => ['nullable', 'string', 'max:150'], 'app_tagline2' => ['nullable', 'string', 'max:300'], 'footer_text' => ['nullable', 'string', 'max:200'], 'default_locale' => ['nullable', 'string', 'max:10'], 'instance_mode' => ['nullable', 'string', 'in:Production,Trial,Demo'], 'app_logo' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], 'app_favicon' => ['nullable', 'image', 'mimes:png,ico,webp', 'max:1024'], 'enable_landing_page' => ['nullable', 'boolean'], 'feature_notification_center' => ['nullable', 'boolean'], 'feature_google_oauth' => ['nullable', 'boolean'], 'google_client_id' => ['nullable', 'string', 'max:255'], 'google_client_secret' => ['nullable', 'string', 'max:255'], 'feature_facebook_oauth' => ['nullable', 'boolean'], 'facebook_app_id' => ['nullable', 'string', 'max:255'], 'facebook_app_secret' => ['nullable', 'string', 'max:255'], 'feature_github_oauth' => ['nullable', 'boolean'], 'github_client_id' => ['nullable', 'string', 'max:255'], 'github_client_secret' => ['nullable', 'string', 'max:255'], 'social_login_callback_url' => ['nullable', 'string', 'max:255'], // Regional 'regional_timezone' => ['nullable', 'string', 'max:100'], 'regional_date_format' => ['nullable', 'string', 'max:20'], 'regional_time_format' => ['nullable', 'string', 'max:20'], // Login Security 'login_max_attempts' => ['nullable', 'integer', 'min:1', 'max:20'], 'login_lockout_duration' => ['nullable', 'integer', 'min:1', 'max:1440'], 'login_lockout_notify' => ['nullable', 'boolean'], 'two_factor_auth' => ['nullable', 'boolean'], 'two_factor_method' => ['nullable', 'string', 'in:email,app'], 'captcha_enabled' => ['nullable', 'boolean'], 'captcha_site_key' => ['nullable', 'string', 'max:255'], 'captcha_secret_key' => ['nullable', 'string', 'max:255'], 'captcha_version' => ['nullable', 'string', 'in:v2,v3'], 'login_log_enabled' => ['nullable', 'boolean'], // Password Policy 'password_min_length' => ['nullable', 'integer', 'min:8', 'max:100'], 'password_max_length' => ['nullable', 'integer', 'min:8', 'max:255'], 'password_require_uppercase' => ['nullable', 'boolean'], 'password_require_lowercase' => ['nullable', 'boolean'], 'password_require_numeric' => ['nullable', 'boolean'], 'password_require_special' => ['nullable', 'boolean'], 'password_expiry_days' => ['nullable', 'integer', 'min:0', 'max:365'], 'password_history_count' => ['nullable', 'integer', 'min:0', 'max:10'], 'password_reset_link_expiry' => ['nullable', 'integer', 'min:1', 'max:10080'], // Session Security 'session_driver' => ['nullable', 'string', 'in:file,redis,database'], 'session_lifetime' => ['nullable', 'integer', 'min:1', 'max:10080'], 'session_single_session' => ['nullable', 'boolean'], 'session_auto_logout_idle' => ['nullable', 'integer', 'min:1', 'max:10080'], 'session_allow_remember_me' => ['nullable', 'boolean'], 'session_remember_me_duration' => ['nullable', 'integer', 'min:1', 'max:365'], 'session_secure_cookie' => ['nullable', 'boolean'], 'session_encrypt' => ['nullable', 'boolean'], 'session_concurrent_limit' => ['nullable', 'integer', 'min:0', 'max:50'], // WebAuthn 'two_factor_trust_days' => ['nullable', 'integer', 'min:1', 'max:365'], 'webauthn_enabled' => ['nullable', 'boolean'], // IP & Access Control 'ip_whitelist_admin' => ['nullable', 'string'], 'ip_blacklist' => ['nullable', 'string'], 'rate_limit_per_ip' => ['nullable', 'integer', 'min:1', 'max:1000'], 'auto_block_ip' => ['nullable', 'boolean'], 'threshold_auto_block' => ['nullable', 'integer', 'min:1', 'max:5000'], 'force_https' => ['nullable', 'boolean'], 'hsts_enabled' => ['nullable', 'boolean'], 'cors_origins' => ['nullable', 'string'], 'cors_methods' => ['nullable', 'string'], 'cors_headers' => ['nullable', 'string'], // Notifications 'mail_driver' => ['nullable', 'string', 'in:smtp,mailgun,ses,mail'], 'mail_host' => ['nullable', 'string', 'max:255'], 'mail_port' => ['nullable', 'integer', 'min:1', 'max:65535'], 'mail_username' => ['nullable', 'string', 'max:255'], 'mail_password' => ['nullable', 'string', 'max:255'], 'mail_encryption' => ['nullable', 'string', 'in:tls,ssl,null'], 'mail_from_address' => ['nullable', 'email', 'max:255'], 'mail_from_name' => ['nullable', 'string', 'max:255'], 'telegram_bot_token' => ['nullable', 'string', 'max:255'], 'telegram_chat_id' => ['nullable', 'string', 'max:255'], // Backups 'backup_db_enabled' => ['nullable', 'boolean'], 'backup_db_driver' => ['nullable', 'string', 'in:local,s3,gdrive'], 'backup_db_frequency' => ['nullable', 'string', 'in:hourly,daily,weekly,monthly'], 'backup_db_time' => ['nullable', 'string', 'regex:/^[0-9]{2}:[0-9]{2}$/'], 'backup_db_retention' => ['nullable', 'integer', 'min:1', 'max:365'], 'backup_db_compress' => ['nullable', 'boolean'], 'backup_db_encrypt' => ['nullable', 'boolean'], 'backup_db_encrypt_key' => ['nullable', 'string', 'min:32', 'max:255'], 'backup_db_exclude' => ['nullable', 'string'], 'backup_db_notify_on' => ['nullable', 'string', 'in:success,failed,both,none'], 'backup_db_notify_to' => ['nullable', 'string', 'max:255'], // Google Drive Backups 'gdrive_client_id' => ['nullable', 'string', 'max:500'], 'gdrive_client_secret' => ['nullable', 'string', 'max:500'], 'gdrive_refresh_token' => ['nullable', 'string', 'max:1000'], 'gdrive_folder' => ['nullable', 'string', 'max:255'], // S3 Backups 's3_key' => ['nullable', 'string', 'max:255'], 's3_secret' => ['nullable', 'string', 'max:255'], 's3_region' => ['nullable', 'string', 'max:100'], 's3_bucket' => ['nullable', 'string', 'max:255'], 's3_endpoint' => ['nullable', 'string', 'max:500'], // Maintenance Mode 'maintenance_mode_enabled' => ['nullable', 'boolean'], 'maintenance_mode_message' => ['nullable', 'string', 'max:1000'], 'maintenance_mode_title' => ['nullable', 'string', 'max:200'], 'maintenance_mode_secret' => ['nullable', 'string', 'alpha_dash', 'max:100'], 'maintenance_mode_allowed_ips' => ['nullable', 'string'], 'maintenance_mode_end_at' => ['nullable', 'date'], 'maintenance_mode_retry' => ['nullable', 'integer', 'min:0', 'max:3600'], 'maintenance_mode_image' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], // Content & Legal 'page_help_content' => ['nullable', 'string'], 'page_tos_content' => ['nullable', 'string'], 'page_privacy_content' => ['nullable', 'string'], 'page_about_content' => ['nullable', 'string'], 'page_security_content' => ['nullable', 'string'], 'require_pdp_on_registration' => ['nullable', 'boolean'], 'feature_cookie_banner' => ['nullable', 'boolean'], 'pdp_document_version' => ['nullable', 'integer', 'min:1'], 'tos_document_version' => ['nullable', 'integer', 'min:1'], 'pdp_dpo_email' => ['nullable', 'email', 'max:255'], 'pdp_company_address' => ['nullable', 'string', 'max:500'], // AI Configuration 'ai_enabled' => ['nullable', 'boolean'], 'ai_provider' => ['nullable', 'string', 'in:gpt,gemini,claude,deepseek,grok,mistral,ollama,openrouter'], 'ai_gpt_key' => ['nullable', 'string', 'max:255'], 'ai_gemini_key' => ['nullable', 'string', 'max:255'], 'ai_claude_key' => ['nullable', 'string', 'max:255'], 'ai_deepseek_key' => ['nullable', 'string', 'max:255'], 'ai_grok_key' => ['nullable', 'string', 'max:255'], 'ai_mistral_key' => ['nullable', 'string', 'max:255'], 'ai_ollama_base_url' => ['nullable', 'string', 'max:255'], 'ai_openrouter_key' => ['nullable', 'string', 'max:255'], 'ai_default_model' => ['nullable', 'string', 'max:100'], 'ai_system_instruction' => ['nullable', 'string', 'max:5000'], 'ai_temperature' => ['nullable', 'numeric', 'min:0', 'max:2'], 'ai_max_tokens' => ['nullable', 'integer', 'min:1', 'max:32000'], // SAP RFC Configuration 'sap_rfc_ashost' => ['nullable', 'string', 'max:255'], 'sap_rfc_sysnr' => ['nullable', 'string', 'max:20'], 'sap_rfc_client' => ['nullable', 'string', 'max:20'], 'sap_rfc_user' => ['nullable', 'string', 'max:255'], 'sap_rfc_passwd' => ['nullable', 'string', 'max:255'], 'sap_rfc_router' => ['nullable', 'string', 'max:255'], 'sap_rfc_trace' => ['nullable', 'string', 'in:0,1,2'], 'engine_pulse_enabled' => ['nullable', 'boolean'], 'engine_telescope_enabled' => ['nullable', 'boolean'], 'engine_swagger_enabled' => ['nullable', 'boolean'], 'engine_horizon_enabled' => ['nullable', 'boolean'], 'ai_healing_enabled' => ['nullable', 'boolean'], ]; } protected function prepareForValidation(): void { $booleans = [ 'feature_notification_center', 'feature_google_oauth', 'feature_facebook_oauth', 'feature_github_oauth', 'login_lockout_notify', 'two_factor_auth', 'captcha_enabled', 'login_log_enabled', 'password_require_uppercase', 'password_require_lowercase', 'password_require_numeric', 'password_require_special', 'webauthn_enabled', 'session_single_session', 'session_allow_remember_me', 'session_secure_cookie', 'session_encrypt', 'auto_block_ip', 'force_https', 'hsts_enabled', 'backup_db_enabled', 'backup_db_compress', 'backup_db_encrypt', 'maintenance_mode_enabled', 'require_pdp_on_registration', 'feature_cookie_banner', 'ai_enabled', 'enable_landing_page', 'engine_pulse_enabled', 'engine_telescope_enabled', 'engine_swagger_enabled', 'engine_horizon_enabled', 'ai_healing_enabled', ]; $updates = []; foreach ($booleans as $key) { $updates[$key] = $this->boolean($key); } if (!empty($updates)) { $this->merge($updates); } } }