GRN2-252: Change to how sign ins are processed (#869)

* Social to local

* Social/Local to Social

* Rubocop fixes

* Added test cases

* Added the ability to clear social uids

* Update admins_controller.rb

* Update admins_controller.rb
This commit is contained in:
Ahmad Farhat 2020-01-22 16:32:56 -05:00 committed by farhatahmad
parent 42e6e4f235
commit 005ec84c73
10 changed files with 181 additions and 3 deletions

View File

@ -175,6 +175,13 @@ class AdminsController < ApplicationController
end end
end end
# POST /admins/clear_auth
def clear_auth
User.include_deleted.where(provider: @user_domain).update_all(social_uid: nil)
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/clear_cache # POST /admins/clear_cache
def clear_cache def clear_cache
Rails.cache.delete("#{@user_domain}/getUser") Rails.cache.delete("#{@user_domain}/getUser")

View File

@ -68,6 +68,18 @@ module Authenticator
session.delete(:user_id) if current_user session.delete(:user_id) if current_user
end end
# Check if the user is using local accounts
def auth_changed_to_local?(user)
Rails.configuration.loadbalanced_configuration && user.social_uid.present? && allow_greenlight_accounts?
end
# Check if the user exists under the same email with no social uid and that social accounts are allowed
def auth_changed_to_social?(email)
Rails.configuration.loadbalanced_configuration &&
User.exists?(email: email, provider: @user_domain, social_uid: nil) &&
!allow_greenlight_accounts?
end
private private
# Migrates all of the twitter users rooms to the new account # Migrates all of the twitter users rooms to the new account

View File

@ -56,6 +56,8 @@ class PasswordResetsController < ApplicationController
# Password does not match password confirmation # Password does not match password confirmation
flash.now[:alert] = I18n.t("password_different_notice") flash.now[:alert] = I18n.t("password_different_notice")
elsif @user.update_attributes(user_params) elsif @user.update_attributes(user_params)
# Clear the user's social uid if they are switching from a social to a local account
@user.update_attribute(:social_uid, nil) if @user.social_uid.present?
# Successfully reset password # Successfully reset password
return redirect_to root_path, flash: { success: I18n.t("password_reset_success") } return redirect_to root_path, flash: { success: I18n.t("password_reset_success") }
end end
@ -66,7 +68,7 @@ class PasswordResetsController < ApplicationController
private private
def find_user def find_user
@user = User.find_by(email: params[:email]) @user = User.find_by(email: params[:email], provider: @user_domain)
end end
def user_params def user_params

View File

@ -74,6 +74,10 @@ class SessionsController < ApplicationController
# Check user with that email exists # Check user with that email exists
return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user
# Check if authenticators have switched
return switch_account_to_local(user) if !is_super_admin && auth_changed_to_local?(user)
# Check correct password was entered # Check correct password was entered
return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user.try(:authenticate, return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user.try(:authenticate,
session_params[:password]) session_params[:password])
@ -199,6 +203,9 @@ class SessionsController < ApplicationController
# If using invitation registration method, make sure user is invited # If using invitation registration method, make sure user is invited
return redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless passes_invite_reqs return redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless passes_invite_reqs
# Switch the user to a social account if they exist under the same email with no social uid
switch_account_to_social if !@user_exists && auth_changed_to_social?(@auth['info']['email'])
user = User.from_omniauth(@auth) user = User.from_omniauth(@auth)
logger.info "Support: Auth user #{user.email} is attempting to login." logger.info "Support: Auth user #{user.email} is attempting to login."
@ -225,4 +232,28 @@ class SessionsController < ApplicationController
end end
end end
end end
# Send the user a password reset email to allow them to set their password
def switch_account_to_local(user)
logger.info "Switching social account to local account for #{user.uid}"
# Send the user a reset password email
user.create_reset_digest
send_password_reset_email(user)
# Overwrite the flash with a more descriptive message if successful
flash[:success] = I18n.t("reset_password.auth_change") if flash[:success].present?
redirect_to signin_path
end
# Set the user's social id to the new id being passed
def switch_account_to_social
user = User.find_by(email: @auth['info']['email'], provider: @user_domain, social_uid: nil)
logger.info "Switching account to social account for #{user.uid}"
# Set the user's social id to the one being returned from auth
user.update_attribute(:social_uid, @auth['uid'])
end
end end

View File

@ -155,6 +155,7 @@
</div> </div>
</div> </div>
<% if current_user.has_role? :super_admin%> <% if current_user.has_role? :super_admin%>
<hr>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="mb-6 form-group"> <div class="mb-6 form-group">
@ -164,4 +165,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mb-4 row">
<div class="col-12">
<div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.clear_auth.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.clear_auth.info") %></label>
<%= button_to t("administrator.site_settings.clear_auth.button"), admin_clear_auth_path, class: "btn btn-primary" %>
</div>
</div>
</div>
<% end %> <% end %>

View File

@ -46,6 +46,10 @@ en:
info: Clears the stored provider cache which forces a new request for the updated info info: Clears the stored provider cache which forces a new request for the updated info
title: Clear Provider Cache title: Clear Provider Cache
button: Clear Cache button: Clear Cache
clear_auth:
info: Clears the current authenticator for users allowing them to sign back in using a different authentication method
title: Clear Current Authenticator
button: Clear Auth
color: color:
info: Changing the Regular Color will change both Lighten and Darken. Lighten and Darken can then be changed individually info: Changing the Regular Color will change both Lighten and Darken. Lighten and Darken can then be changed individually
title: Primary Color title: Primary Color
@ -415,6 +419,7 @@ en:
password: New Password password: New Password
confirm: New Password Confirmation confirm: New Password Confirmation
update: Update Password update: Update Password
auth_change: The authentication method has changed. Please check your email to set your password.
roles: roles:
active: Active active: Active
admin: Admin admin: Admin

View File

@ -55,6 +55,7 @@ Rails.application.routes.draw do
post '/registration_method', to: 'admins#registration_method', as: :admin_change_registration post '/registration_method', to: 'admins#registration_method', as: :admin_change_registration
post '/coloring', to: 'admins#coloring', as: :admin_coloring post '/coloring', to: 'admins#coloring', as: :admin_coloring
post '/clear_cache', to: 'admins#clear_cache', as: :admin_clear_cache post '/clear_cache', to: 'admins#clear_cache', as: :admin_clear_cache
post '/clear_auth', to: 'admins#clear_auth', as: :admin_clear_auth
# Roles # Roles
post '/role', to: 'admins#new_role', as: :admin_new_role post '/role', to: 'admins#new_role', as: :admin_new_role
patch 'roles/order', to: 'admins#change_role_order', as: :admin_roles_order patch 'roles/order', to: 'admins#change_role_order', as: :admin_roles_order

View File

@ -344,6 +344,36 @@ describe AdminsController, type: :controller do
expect(response).to redirect_to(admin_site_settings_path) expect(response).to redirect_to(admin_site_settings_path)
end end
end end
it "clears all users social uids if clear auth button is clicked" do
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
controller.instance_variable_set(:@user_domain, "provider1")
@request.session[:user_id] = @admin.id
@admin.add_role :super_admin
@admin.update_attribute(:provider, "greenlight")
@user2 = create(:user, provider: "provider1")
@user3 = create(:user, provider: "provider1")
@user.update_attribute(:social_uid, Faker::Internet.password)
@user2.update_attribute(:social_uid, Faker::Internet.password)
@user3.update_attribute(:social_uid, Faker::Internet.password)
expect(@user.social_uid).not_to be(nil)
expect(@user2.social_uid).not_to be(nil)
expect(@user3.social_uid).not_to be(nil)
post :clear_auth
@user.reload
@user2.reload
@user3.reload
expect(@user.social_uid).to be(nil)
expect(@user2.social_uid).to be(nil)
expect(@user3.social_uid).to be(nil)
end
end end
describe "Roles" do describe "Roles" do

View File

@ -115,7 +115,7 @@ describe PasswordResetsController, type: :controller do
end end
it "updates attributes if the password update is a success" do it "updates attributes if the password update is a success" do
user = create(:user) user = create(:user, provider: "greenlight")
token = "reset_token" token = "reset_token"
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost

View File

@ -88,7 +88,11 @@ describe SessionsController, type: :controller do
end end
describe "POST #create" do describe "POST #create" do
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) } before do
allow(Rails.configuration).to receive(:enable_email_verification).and_return(true)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_local?).and_return(false)
end
before(:each) do before(:each) do
@user1 = create(:user, provider: 'greenlight', password: 'example', password_confirmation: 'example') @user1 = create(:user, provider: 'greenlight', password: 'example', password_confirmation: 'example')
@user2 = create(:user, password: 'example', password_confirmation: "example") @user2 = create(:user, password: 'example', password_confirmation: "example")
@ -251,6 +255,22 @@ describe SessionsController, type: :controller do
expect(@user1.rooms.find { |r| r.name == "Old Home Room" }).to_not be_nil expect(@user1.rooms.find { |r| r.name == "Old Home Room" }).to_not be_nil
expect(@user1.rooms.find { |r| r.name == "Test" }).to_not be_nil expect(@user1.rooms.find { |r| r.name == "Test" }).to_not be_nil
end end
it "sends the user a reset password email if the authentication method is changing to local" do
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_local?).and_return(true)
email = Faker::Internet.email
create(:user, email: email, provider: "greenlight", social_uid: "google-user")
expect {
post :create, params: {
session: {
email: email,
password: 'example',
},
}
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end end
describe "GET/POST #omniauth" do describe "GET/POST #omniauth" do
@ -428,6 +448,66 @@ describe SessionsController, type: :controller do
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
it "switches a social account to a different social account if the authentication method changed" do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:bn_launcher]
get :omniauth, params: { provider: 'bn_launcher' }
u = User.find_by(social_uid: "bn-launcher-user")
u.social_uid = nil
users_old_uid = u.uid
u.save!
new_user = OmniAuth::AuthHash.new(
provider: "bn_launcher",
uid: "bn-launcher-user-new",
info: {
email: "user@google.com",
name: "Office User",
nickname: "googleuser",
image: "touch.png",
customer: 'customer1',
}
)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_social?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("customer1")
controller.instance_variable_set(:@user_domain, "customer1")
request.env["omniauth.auth"] = new_user
get :omniauth, params: { provider: 'bn_launcher' }
new_u = User.find_by(social_uid: "bn-launcher-user-new")
expect(users_old_uid).to eq(new_u.uid)
end
it "switches a local account to a different social account if the authentication method changed" do
email = Faker::Internet.email
user = create(:user, email: email, provider: "customer1")
users_old_uid = user.uid
new_user = OmniAuth::AuthHash.new(
provider: "bn_launcher",
uid: "bn-launcher-user-new",
info: {
email: email,
name: "Office User",
nickname: "googleuser",
image: "touch.png",
customer: 'customer1',
}
)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_social?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("customer1")
controller.instance_variable_set(:@user_domain, "customer1")
request.env["omniauth.auth"] = new_user
get :omniauth, params: { provider: 'bn_launcher' }
new_u = User.find_by(social_uid: "bn-launcher-user-new")
expect(users_old_uid).to eq(new_u.uid)
end
end end
describe "POST #ldap" do describe "POST #ldap" do