Skip to content

Commit

Permalink
Add tracking system language to the algorithm calculating the match s…
Browse files Browse the repository at this point in the history
…core
  • Loading branch information
Patrycja-Nowak committed Nov 24, 2023
1 parent f35bf28 commit 65db1cb
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 9 deletions.
6 changes: 4 additions & 2 deletions app/controllers/devise_recognisable/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def check_for_authentication_token
sign_in_ip: request.location.ip,
sign_in_at: Time.now,
user_agent: request.user_agent,
accept_header: request.headers["HTTP_ACCEPT"]
accept_header: request.headers["HTTP_ACCEPT"],
accept_language: request.headers["Accept-Language"].first
)
redirect_to after_sign_in_path_for(resource)
end
Expand All @@ -69,7 +70,8 @@ def store_recognisable_details
sign_in_ip: request.location.ip,
sign_in_at: Time.now,
user_agent: request.user_agent,
accept_header: request.headers["HTTP_ACCEPT"]
accept_header: request.headers["HTTP_ACCEPT"],
accept_language: request.headers["Accept-Language"]
)
end
end
17 changes: 14 additions & 3 deletions app/lib/devise_recognisable/guard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class DeviseRecognisable::Guard
MAX_LEVENSHTEIN_DISTANCE = Rails.env.test? ? 10 : 0

@@required_scores = {
relaxed: 2,
normal: 3,
strict: 4
relaxed: 3,
normal: 4,
strict: 5
}

def self.with(previous_sessions)
Expand Down Expand Up @@ -61,6 +61,9 @@ def calculate_score_for(session)
# Is the request's Accept header different to the previous sign in?
score += 1 if session.accept_header == @request.headers["HTTP_ACCEPT"]

# Is the request's Accept-Language header different to the previous sign in?
score += 1 if session.accept_language == @request.headers["Accept-Language"]

return score
end

Expand Down Expand Up @@ -165,6 +168,14 @@ def failures
}
end

# Is the request's Accept-Language header different to the previous sign in?
unless @closest_match[:session].accept_language == @request.headers["Accept-Language"]
failures[:failures][:accept_language] = {
request_value: @request.headers["Accept-Language"],
session_value: @closest_match[:session].accept_language
}
end

return failures
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CreateRecognisableSessions < <%= "ActiveRecord::Migration[#{ActiveRecord::
t.<%= ip_column %> :sign_in_ip
t.string :user_agent
t.string :accept_header
t.string :accept_language
t.datetime :sign_in_at
end
add_index :recognisable_sessions, [:recognisable_type, :recognisable_id], :name => 'recognisable_index'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def change
t.string :sign_in_ip
t.string :user_agent
t.string :accept_header
t.string :accept_language
t.datetime :sign_in_at
end
add_index :recognisable_sessions, [:recognisable_type, :recognisable_id], :name => 'recognisable_index'
Expand Down
1 change: 1 addition & 0 deletions spec/dummy-app/db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 37 additions & 4 deletions spec/dummy-app/spec/features/sign_in_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@

let!(:user_agent) { 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36' }
let!(:accept_header) { 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' }
let!(:accept_language) { 'en' }
let!(:recognisable_session_values) {{
recognisable_id: user.id,
recognisable_type: 'User',
user_agent: user_agent,
accept_header: accept_header
accept_header: accept_header,
accept_language: accept_language
}}

before do
Capybara.current_session.driver.header('User-Agent', user_agent)
Capybara.current_session.driver.header('Accept', accept_header)
Capybara.current_session.driver.header('Accept-Language', accept_language)
end

context 'as a user that has no last_sign_in_ip' do
Expand Down Expand Up @@ -169,7 +172,7 @@
:session_value=>new_ip,
:comparison_result=>:complete_mismatch
}},
:score=>2,
:score=>3,
:user_id=>1,
:user_type=>"User"
), "Unrecognised request")
Expand Down Expand Up @@ -224,7 +227,7 @@
:session_value=>new_ip,
:comparison_result=>:complete_mismatch
}},
:score=>2,
:score=>3,
:user_id=>1,
:user_type=>"User"
), "Unrecognised request")
Expand Down Expand Up @@ -297,6 +300,7 @@
context 'from a device with a different Accept header value' do
let!(:recognisable_session) { FactoryBot.create :recognisable_session, recognisable_session_values }
let!(:new_accept_header) { 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' }


before do
recognisable_session.update!(accept_header: new_accept_header)
Expand All @@ -321,14 +325,43 @@
end
end

context 'from a device with a different Accept-Language header value' do
let!(:recognisable_session) { FactoryBot.create :recognisable_session, recognisable_session_values }
let!(:new_accept_language) { 'fr' }

before do
recognisable_session.update!(accept_header: new_accept_language)
visit '/'
click_link 'Log in'
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
end

it 'does not log the user in' do
expect(page).to have_content I18n.t('devise.sessions.send_new_ip_instructions')
expect(page).to_not have_content('Home sweet home')
end

context 'visiting the link in the email' do
it 'logs the user in' do
open_email(user.email, with_subject: I18n.t('devise.mailer.new_ip.subject'))
visit_in_email('Log in')
expect(page).to have_content('Home sweet home')
end
end
end

context 'with multiple different RecognisableSessions' do
let!(:different_user_agent) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko)' }
let!(:different_accept_header) { 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' }
let!(:different_accept_language) { 'fr' }
let!(:different_recognisable_session_values) {{
recognisable_id: user.id,
recognisable_type: 'User',
user_agent: different_user_agent,
accept_header: different_accept_header
accept_header: different_accept_header,
accept_language: different_accept_language
}}

before do
Expand Down

0 comments on commit 65db1cb

Please sign in to comment.