FEATURE: Suspicious logins report. (#6544)

This commit is contained in:
Bianca Nenciu 2018-10-31 00:51:58 +02:00 committed by Régis Hanol
parent f2b7254a99
commit e0ccd36dbe
5 changed files with 116 additions and 0 deletions

View File

@ -4,6 +4,12 @@ module Jobs
def execute(args)
if UserAuthToken.is_suspicious(args[:user_id], args[:client_ip])
UserAuthToken.log(action: 'suspicious',
user_id: args[:user_id],
user_agent: args[:user_agent],
client_ip: args[:client_ip])
Jobs.enqueue(:critical_user_email,
type: :suspicious_login,
user_id: args[:user_id],

View File

@ -1236,6 +1236,79 @@ class Report
end
end
def self.report_suspicious_logins(report)
report.modes = [:table]
report.labels = [
{
type: :user,
properties: {
username: :username,
id: :user_id,
avatar: :avatar_template,
},
title: I18n.t("reports.suspicious_logins.labels.user")
},
{
property: :client_ip,
title: I18n.t("reports.suspicious_logins.labels.client_ip")
},
{
property: :location,
title: I18n.t("reports.suspicious_logins.labels.location")
},
{
property: :browser,
title: I18n.t("reports.suspicious_logins.labels.browser")
},
{
property: :device,
title: I18n.t("reports.suspicious_logins.labels.device")
},
{
property: :os,
title: I18n.t("reports.suspicious_logins.labels.os")
},
{
type: :date,
property: :login_time,
title: I18n.t("reports.suspicious_logins.labels.login_time")
},
]
report.data = []
sql = <<~SQL
SELECT u.id user_id, u.username, u.uploaded_avatar_id, t.client_ip, t.user_agent, t.created_at login_time
FROM user_auth_token_logs t
JOIN users u ON u.id = t.user_id
WHERE t.action = 'suspicious'
AND t.created_at >= '#{report.start_date}'
AND t.created_at <= '#{report.end_date}'
SQL
DB.query(sql).each do |row|
data = {}
ipinfo = DiscourseIpInfo.get(row.client_ip)
browser = BrowserDetection.browser(row.user_agent)
device = BrowserDetection.device(row.user_agent)
os = BrowserDetection.os(row.user_agent)
data[:username] = row.username
data[:user_id] = row.user_id
data[:avatar_template] = User.avatar_template(row.username, row.uploaded_avatar_id)
data[:client_ip] = row.client_ip.to_s
data[:location] = ipinfo[:location]
data[:browser] = I18n.t("user_auth_tokens.browser.#{browser}")
data[:device] = I18n.t("user_auth_tokens.device.#{device}")
data[:os] = I18n.t("user_auth_tokens.os.#{os}")
data[:login_time] = row.login_time
report.data << data
end
end
private
def hex_to_rgbs(hex_color)

View File

@ -1118,6 +1118,16 @@ en:
labels:
user_agent: "User Agent"
page_views: "Pageviews"
suspicious_logins:
title: "Suspcious Logins"
labels:
user: User
client_ip: Client IP
location: Location
browser: Browser
device: Device
os: Operating System
login_time: Login Time
dashboard:
rails_env_warning: "Your server is running in %{env} mode."

View File

@ -13,6 +13,8 @@ describe Jobs::SuspiciousLogin do
it "will not send an email on first login" do
Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :suspicious_login)).never
described_class.new.execute(user_id: user.id, client_ip: "1.1.1.1")
expect(UserAuthTokenLog.where(action: "suspicious").count).to eq(0)
end
it "will not send an email when user log in from a known location" do
@ -21,6 +23,8 @@ describe Jobs::SuspiciousLogin do
Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :suspicious_login)).never
described_class.new.execute(user_id: user.id, client_ip: "1.1.1.1")
described_class.new.execute(user_id: user.id, client_ip: "1.1.1.2")
expect(UserAuthTokenLog.where(action: "suspicious").count).to eq(0)
end
it "will send an email when user logs in from a new location" do
@ -28,6 +32,8 @@ describe Jobs::SuspiciousLogin do
Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :suspicious_login))
described_class.new.execute(user_id: user.id, client_ip: "1.1.2.1")
expect(UserAuthTokenLog.where(action: "suspicious").count).to eq(1)
end
end

View File

@ -973,4 +973,25 @@ describe Report do
end
end
end
describe "report_suspicious_logins" do
let(:joffrey) { Fabricate(:user, username: "joffrey") }
let(:robin) { Fabricate(:user, username: "robin") }
context "with data" do
it "works" do
UserAuthToken.log(action: "suspicious", user_id: robin.id)
UserAuthToken.log(action: "suspicious", user_id: joffrey.id)
UserAuthToken.log(action: "suspicious", user_id: joffrey.id)
report = Report.find("suspicious_logins")
expect(report.data.length).to eq(3)
expect(report.data[0][:username]).to eq("robin")
expect(report.data[1][:username]).to eq("joffrey")
expect(report.data[2][:username]).to eq("joffrey")
end
end
end
end