Skip to content

Commit

Permalink
Warn on suspicious login, after long period of inactivity
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinrobinson committed Mar 6, 2019
1 parent a8eff2f commit 24122f1
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 2 deletions.
8 changes: 6 additions & 2 deletions app/lib/ldap_authenticatable_tiny/strategy.rb
Expand Up @@ -60,9 +60,9 @@ def authenticate_without_consistent_timing!
ldap_login = PerDistrict.new.ldap_login_for_educator(educator) ldap_login = PerDistrict.new.ldap_login_for_educator(educator)
return fail!(:invalid) unless is_authorized_by_ldap?(ldap_login, password_text) return fail!(:invalid) unless is_authorized_by_ldap?(ldap_login, password_text)


# Success, run password checks and store results encrypted and noised, # Success, run security checks
# ignoring any errors in the process.
store_password_check(password_text) store_password_check(password_text)
warn_if_suspicious(educator)


# Return success # Return success
return success!(educator) return success!(educator)
Expand Down Expand Up @@ -106,6 +106,10 @@ def store_password_check(password_text)
nil nil
end end


def warn_if_suspicious(educator)
LoginChecker.new(educator).warn_if_suspicious
end

def logger def logger
Rails.logger Rails.logger
end end
Expand Down
29 changes: 29 additions & 0 deletions app/lib/login_checker.rb
@@ -0,0 +1,29 @@
# Check for suspicious bits about the login, for warning.
class LoginChecker
def initialize(educator, options = {})
@educator = educator
@time_now = options.fetch(:time_now, Time.now)
end

def warn_if_suspicious
flags = infer_flags
warn_about(flags)
flags
end

private
def infer_flags
last_login_at = LoginActivity.last_login_at(@educator)

flags = []
flags << :first_login_month_after_creation if last_login_at.nil? && @educator.created_at < (@time_now - 30.days)
flags << :first_login_after_year if last_login_at.present? && last_login_at < (@time_now - 1.year)
flags.sort
end

def warn_about(flags)
if flags.size > 0
Rollbar.warn('LoginChecker#warn_if_suspicious', flags: flags)
end
end
end
9 changes: 9 additions & 0 deletions app/models/login_activity.rb
Expand Up @@ -14,4 +14,13 @@ class LoginActivity < ApplicationRecord
belongs_to :user, belongs_to :user,
polymorphic: true, polymorphic: true,
optional: true optional: true

# Return ActiveSupport::TimeWithZone or nil
def self.last_login_at(educator_id)
LoginActivity.where(user_id: educator_id)
.order(created_at: :desc)
.limit(1)
.first
.try(:created_at)
end
end end
26 changes: 26 additions & 0 deletions spec/lib/login_checker_spec.rb
@@ -0,0 +1,26 @@
require 'spec_helper'

RSpec.describe LoginChecker do
let!(:pals) { TestPals.create! }

it '#warn_if_suspicious reports to Rollbar on :first_login_after_year' do
LoginActivity.create!({
user_id: pals.healey_vivian_teacher.id,
created_at: pals.time_now - 2.years
})

allow(Rollbar).to receive(:warn)
expect(Rollbar).to receive(:warn).once.with('LoginChecker#warn_if_suspicious', flags: [:first_login_after_year])
checker = LoginChecker.new(pals.healey_vivian_teacher, time_now: pals.time_now)
expect(checker.warn_if_suspicious).to eq [:first_login_after_year]
end

it '#warn_if_suspicious reports to Rollbar on :first_login_month_after_creation' do
pals.healey_vivian_teacher.update!(created_at: pals.time_now - 32.days)

allow(Rollbar).to receive(:warn)
expect(Rollbar).to receive(:warn).once.with('LoginChecker#warn_if_suspicious', flags: [:first_login_month_after_creation])
checker = LoginChecker.new(pals.healey_vivian_teacher, time_now: pals.time_now)
expect(checker.warn_if_suspicious).to eq [:first_login_month_after_creation]
end
end

0 comments on commit 24122f1

Please sign in to comment.