Questo plugin è destinato all’invio di un avviso via email a tutti gli amministratori ogni volta che un utente accede da un indirizzo IP sospetto. Estrae l’IP dall’ultimo token di autenticazione (utilizzando il campo client_ip), controlla questo IP rispetto a una blocklist definita nelle impostazioni del sito (ip_alert_suspicious_ips) e, se l’IP corrisponde a uno schema bloccato, invia un’email di avviso a tutti gli utenti amministratori.
Errore Osservato:
Nonostante il codice del plugin sembri corretto e segua i normali schemi dei plugin di Discourse, riscontro ancora l’errore generico “Oops – Il software che alimenta questo forum di discussione ha riscontrato un problema imprevisto” al momento dell’accesso. I log degli errori non mostrano indicazioni chiare dai nostri messaggi di log [DiscourseIpAlert] e lo stack trace non individua un problema direttamente nel nostro codice del plugin.
Stato Attuale:
Ho rivisto e corretto il plugin più volte, inclusa la verifica delle impostazioni del sito e l’assicurazione che l’hook (on(:user_logged_in)) sia registrato correttamente. Anche l’estrazione dell’IP e la logica di invio delle email sembrano implementate correttamente, tuttavia l’errore 500 Internal Server Error persiste senza uno stack trace chiaro che lo colleghi al nostro plugin.
Qualcuno può aiutarmi?
plugin.rb:
# frozen_string_literal: true
# name: discourse-ip-alert
# about: Sends an email alert when a user logs in from a suspicious IP address.
# version: 0.7
# authors: OrkoGrayskull (revised)
# required_version: 3.5.0
enabled_site_setting :ip_alert_enabled
enabled_site_setting :ip_alert_suspicious_ips
module ::DiscourseIpAlert
PLUGIN_NAME = "discourse-ip-alert"
require 'ipaddr'
require 'mail'
# Estrae l'indirizzo IP dall'ultimo token di autenticazione
def self.extract_ip(user)
token = UserAuthToken.where(user_id: user.id).order(created_at: :desc).first
ip = token&.attributes["client_ip"]
Rails.logger.info("[DiscourseIpAlert] extract_ip: Extracted IP from UserAuthToken: #{ip.inspect}")
ip
rescue => e
Rails.logger.error("[DiscourseIpAlert] extract_ip error: #{e.message}")
nil
end
# Controlla se l'indirizzo IP fornito è presente nella blocklist
def self.ip_blocked?(ip_address)
return false if ip_address.blank?
blocked_ips = SiteSetting.ip_alert_suspicious_ips.split(",").map(&:strip)
Rails.logger.info("[DiscourseIpAlert] ip_blocked?: Blocked IPs: #{blocked_ips.inspect}")
blocked_ips.any? do |blocked|
if blocked.include?('*')
regex = Regexp.new("^" + Regexp.escape(blocked).gsub('\*', '\d{1,3}') + "$")
Rails.logger.info("[DiscourseIpAlert] ip_blocked?: Comparing #{ip_address} with wildcard regex #{regex.inspect}")
ip_address.match?(regex)
else
begin
result = IPAddr.new(blocked).include?(ip_address)
Rails.logger.info("[DiscourseIpAlert] ip_blocked?: Comparing #{ip_address} with #{blocked}: #{result}")
result
rescue IPAddr::InvalidAddressError => e
Rails.logger.error("[DiscourseIpAlert] ip_blocked?: Invalid IP in blocklist: #{blocked} (#{e.message})")
false
end
end
end
rescue => e
Rails.logger.error("[DiscourseIpAlert] ip_blocked? error: #{e.message}")
false
end
# Invia un'email di avviso a tutti gli amministratori
def self.send_warning_email(user, ip_address)
admin_emails = User.where(admin: true).pluck(:email)
return if admin_emails.empty?
subject = "⚠️ Warning: Login from a Suspicious IP!"
body = <<~BODY
Attention!
The user #{user.username} (#{user.email}) has logged in from the IP address #{ip_address},
which has been flagged as suspicious.
Please review this account.
BODY
admin_emails.each do |email|
begin
mail = Mail.new do
from ENV['DISCOURSE_NOTIFICATION_EMAIL'] || 'no-reply@example.org'
to email
subject subject
body body
end
Rails.logger.info("[DiscourseIpAlert] Sending warning email to #{email}")
mail.deliver!
rescue => e
Rails.logger.error("[DiscourseIpAlert] Error sending email to #{email}: #{e.message}")
end
end
rescue => e
Rails.logger.error("[DiscourseIpAlert] send_warning_email error: #{e.message}")
end
# Elabora il login dell'utente: estrae l'IP, controlla la blocklist e invia un'email di avviso se necessario
def self.process_user_login(user)
Rails.logger.info("[DiscourseIpAlert] process_user_login for user: #{user.username}")
ip_address = extract_ip(user)
unless ip_address
Rails.logger.warn("[DiscourseIpAlert] No IP address extracted – skipping further processing.")
return
end
if ip_blocked?(ip_address)
Rails.logger.warn("[DiscourseIpAlert] Suspicious IP detected: #{ip_address}")
send_warning_email(user, ip_address)
else
Rails.logger.info("[DiscourseIpAlert] IP #{ip_address} is considered safe.")
end
rescue => e
Rails.logger.error("[DiscourseIpAlert] process_user_login error: #{e.message}")
end
end
after_initialize do
Rails.logger.info("[DiscourseIpAlert] after_initialize: Plugin is being integrated into the login process.")
# Registra l'hook che viene chiamato ad ogni login dell'utente
on(:user_logged_in) do |user|
begin
Rails.logger.info("[DiscourseIpAlert] on(:user_logged_in) hook called for user: #{user.username}")
::DiscourseIpAlert.process_user_login(user)
rescue => e
Rails.logger.error("[DiscourseIpAlert] Error in on(:user_logged_in) hook: #{e.message}")
end
end
end
config/settings.yml:
ip_alert_enabled:
default: false
client: true
type: bool
description: "Enable the IP alert"
ip_alert_suspicious_ips:
default: "192.168.1.1,203.0.113.0/24"
client: true
type: string
description: "List of suspicious IP addresses (comma-separated)"