Skip to content

Commit

Permalink
Merge branch 'rc-release-5.8.10' into fix_broker_can_update_policy
Browse files Browse the repository at this point in the history
  • Loading branch information
sri49 authored Aug 6, 2024
2 parents 0edf844 + 7c9dcec commit 604437a
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 10 deletions.
16 changes: 16 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def format_js?
protect_from_forgery with: :exception

## Devise filters
before_action :check_concurrent_sessions
before_action :require_login, unless: :authentication_not_required?
before_action :authenticate_user_from_token!
before_action :authenticate_me!
Expand Down Expand Up @@ -98,6 +99,21 @@ def create_sso_account(user, personish, timeout, account_role = "individual")

private

def check_concurrent_sessions
return unless EnrollRegistry.feature_enabled?(:prevent_concurrent_sessions) && concurrent_sessions? && current_user.has_hbx_staff_role?

flash[:error] = l10n('devise.sessions.signed_out_concurrent_session')
sign_out current_user
end

def concurrent_sessions?
# If the session token differs from the token stored in the db
# a new login for this user is detected.
# Checking for User class prevents spec breaking for Doubles.
# Currently only enabled for admin users.
current_user.instance_of?(User) && (session[:login_token] != current_user&.current_login_token)
end

def strong_params
params.permit!
end
Expand Down
28 changes: 21 additions & 7 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,33 @@ class Users::SessionsController < Devise::SessionsController

after_action :log_failed_login, :only => :new
before_action :set_ie_flash_by_announcement, only: [:new]
skip_before_action :check_concurrent_sessions

def new
super do
# Persist flash error message when signing out user
flash.keep(:error) if flash[:error].present? && flash[:error] == l10n('devise.sessions.signed_out_concurrent_session')
end
end

def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
location = after_sign_in_path_for(resource)
flash[:warning] = current_user.get_announcements_by_roles_and_portal(location) if current_user.present?
respond_with resource, location: location
super do
flash.delete(:notice) unless is_flashing_format?
set_login_token
location = after_sign_in_path_for(resource)
flash[:warning] = current_user.get_announcements_by_roles_and_portal(location) if current_user.present?
end
end

private

def set_login_token
# Set devise session token to prevent concurrent user logins.
token = Devise.friendly_token
session[:login_token] = token
current_user.update_attributes!(current_login_token: token)
end

def log_failed_login
return unless failed_login?

Expand Down
3 changes: 3 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ def switch_to_idp!
## Seed: https://github.com/plataformatec/devise/wiki/How-To%3a-Require-admin-to-activate-account-before-sign_in
field :approved, type: Boolean, default: true

# Session token for Devise to prevent concurrent user sessions
field :current_login_token

##RIDP
field :identity_verified_date, type: Date
field :identity_final_decision_code, type: String
Expand Down
2 changes: 1 addition & 1 deletion app/views/devise/registrations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
</div>

<script type="text/javascript">
$(document).on('ready page:load', function () {
$(document).on('ready turbolinks:load', function () {
Register.initialize();
});
</script>
1 change: 1 addition & 0 deletions db/seedfiles/translations/en/cca/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
"en.devise.shared.links.create_account" => "Create account",
"en.devise.shared.links.did_not_receive_confirmation_instructions" => "Didn't receive confirmation instructions",
"en.devise.shared.links.sign_in_with_provider" => "Sign in with %{provider}",
"en.devise.sessions.signed_out_concurrent_session" => "New user login detected - you have been signed out of this session."
}
1 change: 1 addition & 0 deletions db/seedfiles/translations/en/dc/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
"en.devise.shared.links.create_account" => "Create account",
"en.devise.shared.links.did_not_receive_confirmation_instructions" => "Didn't receive confirmation instructions",
"en.devise.shared.links.sign_in_with_provider" => "Sign in with %{provider}",
"en.devise.sessions.signed_out_concurrent_session" => "New user login detected - you have been signed out of this session."
}
17 changes: 17 additions & 0 deletions features/hbx_admin/concurrent_logins.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Feature: Admin will be signed out if concurrent logins are detected
Background:
Given the prevent_concurrent_sessions feature is enabled
Given that a user with a HBX staff role with hbx_tier3 subrole exists

Scenario: Admin logs in from a second location
Given admin logs in on browser 1
And admin logs in on browser 2
And admin attempts to navigate on browser 1
Then admin on browser 1 should see the logged out due to concurrent session message

Scenario: Admin logs in from a second location while feature is disabled
Given the prevent_concurrent_sessions feature is disabled
Given admin logs in on browser 1
And admin logs in on browser 2
And admin attempts to navigate on browser 1
Then admin on browser 1 should not see the logged out due to concurrent session message
62 changes: 62 additions & 0 deletions features/hbx_admin/step_definitions/sessions_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

Given(/^that a user with a HBX staff role with (.*) subrole exists$/) do |subrole|
p_staff = if ['super_admin', 'hbx_tier3'].include?(subrole)
Permission.create(name: subrole,
modify_family: true, modify_employer: true, revert_application: true, list_enrollments: true,
send_broker_agency_message: true, approve_broker: true, approve_ga: true, can_update_ssn: true,
can_complete_resident_application: true, can_add_sep: true, can_lock_unlock: true,
can_view_username_and_email: true, can_reset_password: true,
modify_admin_tabs: true, view_admin_tabs: true, can_extend_open_enrollment: true, view_the_configuration_tab: true,
can_submit_time_travel_request: false, can_change_username_and_email: true, view_login_history: true)
else
Permission.create(name: subrole, modify_family: true, modify_employer: true, revert_application: true,
list_enrollments: true, send_broker_agency_message: true, approve_broker: true, approve_ga: true, can_update_ssn: true,
can_complete_resident_application: false, can_add_sep: false, can_lock_unlock: true,
can_view_username_and_email: true, can_reset_password: false, can_extend_open_enrollment: true, view_the_configuration_tab: true,
modify_admin_tabs: true, view_admin_tabs: true, can_submit_time_travel_request: false, can_change_username_and_email: true,
view_login_history: true)
end
person = people['Hbx Admin']
hbx_profile = FactoryBot.create(:hbx_profile)
user = FactoryBot.create(:user, :with_family, :hbx_staff, email: person[:email], password: person[:password], password_confirmation: person[:password])
FactoryBot.create(:hbx_staff_role, person: user.person, hbx_profile: hbx_profile, permission_id: p_staff.id)
FactoryBot.create(:hbx_enrollment, household: user.primary_family.active_household)
end

Given(/^the prevent_concurrent_sessions feature is (.*)?/) do |is_enabled|
is_enabled == "enabled" ? enable_feature(:prevent_concurrent_sessions) : disable_feature(:prevent_concurrent_sessions)
end

# rubocop:disable Style/GlobalVars
Given(/^admin logs in on browser (.*)?/) do |session_id|
in_session(session_id) do
person = people["Hbx Admin"]
session = $sessions[session_id]
session.visit "/users/sign_in"
session.fill_in SignIn.username, :with => person[:email]
session.find('#user_login').set(person[:email])
session.fill_in SignIn.password, :with => person[:password]
session.fill_in SignIn.username, :with => person[:email] unless session.find(:xpath, '//*[@id="user_login"]').value == person[:email]
session.find(".interaction-click-control-sign-in", wait: 5).click
end
end

And(/^admin attempts to navigate on browser (.*)?/) do |session_id|
in_session(session_id) do
session = $sessions[session_id]
session.visit exchanges_hbx_profiles_root_path
end
end

Then(/^admin on browser (.*) should (.*) the logged out due to concurrent session message?/) do |session_id, visibility|
in_session(session_id) do
session = $sessions[session_id]
if visibility == "see"
expect(session).to have_content(l10n('devise.sessions.signed_out_concurrent_session'))
else
expect(session).not_to have_content(l10n('devise.sessions.signed_out_concurrent_session'))
end
end
end
# rubocop:enable Style/GlobalVars
16 changes: 16 additions & 0 deletions features/support/capybara_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ def l10n(translation_key, interpolated_keys = {})
rescue I18n::MissingTranslationData
translation_key.gsub(/\W+/, '').titleize
end

# rubocop:disable Style/GlobalVars
def select_session(id)
Capybara.instance_variable_set("@session_pool", {"#{Capybara.current_driver}#{Capybara.app.object_id}" => $sessions[id]})
end

def in_session(id)
$sessions ||= {}
$sessions[:default] ||= Capybara.current_session
$sessions[id] ||= Capybara::Session.new(Capybara.current_driver, Capybara.app)

select_session(id)
yield
select_session(:default)
end
# rubocop:enable Style/GlobalVars
end

World(CapybaraHelpers)
29 changes: 29 additions & 0 deletions features/support/object_model_pages/sign_in.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

#users/sign_in
class SignIn

def self.username
'user[login]'
end

def self.password
'user[password]'
end

def self.remember_me_checkbox
'#user_remember_me'
end

def self.sign_in_btn
'.sign-in-btn'
end

def self.forgot_your_password_btn
'a[href="/users/password/new"]'
end

def self.create_account_btn
'a[href="/users/sign_up"]'
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
context 'Success' do
before :each do
allow(::EnrollRegistry).to receive(:feature_enabled?).with(:benefit_application_reinstate).and_return(true)
allow(EnrollRegistry).to receive(:feature_enabled?).with(:prevent_concurrent_sessions).and_return(false)
allow(hbx_staff_role).to receive(:permission).and_return(double('Permission', can_modify_plan_year: true))
sign_in(user)
initial_application.update_attributes!(:aasm_state => :terminated)
Expand Down Expand Up @@ -225,6 +226,7 @@
context 'when feature enabled' do
before :each do
allow(::EnrollRegistry).to receive(:feature_enabled?).with(:benefit_application_history).and_return(true)
allow(EnrollRegistry).to receive(:feature_enabled?).with(:prevent_concurrent_sessions).and_return(false)
allow(hbx_staff_role).to receive(:permission).and_return(double('Permission', can_modify_plan_year: true))
sign_in(user)
put :application_history, params: { employer_application_id: initial_application.id, employer_id: benefit_sponsorship.id }
Expand All @@ -239,6 +241,7 @@
context 'when feature disabled' do
before :each do
allow(::EnrollRegistry).to receive(:feature_enabled?).with(:benefit_application_history).and_return(false)
allow(EnrollRegistry).to receive(:feature_enabled?).with(:prevent_concurrent_sessions).and_return(false)
allow(hbx_staff_role).to receive(:permission).and_return(double('Permission', can_modify_plan_year: true))
sign_in(user)
put :application_history, params: { employer_application_id: initial_application.id, employer_id: benefit_sponsorship.id }
Expand All @@ -254,4 +257,4 @@
end
end
end
end
end
5 changes: 4 additions & 1 deletion system/config/templates/features/enroll_app/enroll_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ registry:
is_enabled: true
- key: :upload_file_size_limit_in_mb
item: <%= ENV['UPLOAD_FILE_SIZE_LIMIT_IN_MB'] || 10 %>
is_enabled: true
is_enabled: true
- key: :prevent_concurrent_sessions
item: :prevent_concurrent_sessions
is_enabled: <%= ENV['PREVENT_CONCURRENT_SESSIONS_IS_ENABLED'] || false %>

0 comments on commit 604437a

Please sign in to comment.