Skip to content

Commit

Permalink
Coverage for Auth::OmniauthCallbacks controller (mastodon#26147)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjankowski authored Jul 25, 2023
1 parent f826a95 commit 50ff3d3
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 11 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ jobs:
PAM_ENABLED: true
PAM_DEFAULT_SERVICE: pam_test
PAM_CONTROLLED_SERVICE: pam_test_controlled
OIDC_ENABLED: true
OIDC_SCOPE: read
SAML_ENABLED: true
CAS_ENABLED: true
BUNDLE_WITH: 'pam_authentication test'
CI_JOBS: ${{ matrix.ci_job }}/4

Expand Down
39 changes: 28 additions & 11 deletions app/controllers/auth/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,13 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController

def self.provides_callback_for(provider)
define_method provider do
@provider = provider
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user)

if @user.persisted?
LoginActivity.create(
user: @user,
success: true,
authentication_method: :omniauth,
provider: provider,
ip: request.remote_ip,
user_agent: request.user_agent
)

record_login_activity
sign_in_and_redirect @user, event: :authentication
label = Devise.omniauth_configs[provider]&.strategy&.display_name.presence || I18n.t("auth.providers.#{provider}", default: provider.to_s.chomp('_oauth2').capitalize)
set_flash_message(:notice, :success, kind: label) if is_navigational_format?
set_flash_message(:notice, :success, kind: label_for_provider) if is_navigational_format?
else
session["devise.#{provider}_data"] = request.env['omniauth.auth']
redirect_to new_user_registration_url
Expand All @@ -38,4 +30,29 @@ def after_sign_in_path_for(resource)
auth_setup_path(missing_email: '1')
end
end

private

def record_login_activity
LoginActivity.create(
user: @user,
success: true,
authentication_method: :omniauth,
provider: @provider,
ip: request.remote_ip,
user_agent: request.user_agent
)
end

def label_for_provider
provider_display_name || configured_provider_name
end

def provider_display_name
Devise.omniauth_configs[@provider]&.strategy&.display_name.presence
end

def configured_provider_name
I18n.t("auth.providers.#{@provider}", default: @provider.to_s.chomp('_oauth2').capitalize)
end
end
124 changes: 124 additions & 0 deletions spec/requests/omniauth_callbacks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# frozen_string_literal: true

require 'rails_helper'

describe 'OmniAuth callbacks' do
shared_examples 'omniauth provider callbacks' do |provider|
subject { post send "user_#{provider}_omniauth_callback_path" }

context 'with full information in response' do
before do
mock_omniauth(provider, {
provider: provider.to_s,
uid: '123',
info: {
verified: 'true',
email: '[email protected]',
},
})
end

context 'without a matching user' do
it 'creates a user and an identity and redirects to root path' do
expect { subject }
.to change(User, :count)
.by(1)
.and change(Identity, :count)
.by(1)
.and change(LoginActivity, :count)
.by(1)

expect(User.last.email).to eq('[email protected]')
expect(Identity.find_by(user: User.last).uid).to eq('123')
expect(response).to redirect_to(root_path)
end
end

context 'with a matching user and no matching identity' do
before do
Fabricate(:user, email: '[email protected]')
end

it 'matches the existing user, creates an identity, and redirects to root path' do
expect { subject }
.to not_change(User, :count)
.and change(Identity, :count)
.by(1)
.and change(LoginActivity, :count)
.by(1)

expect(Identity.find_by(user: User.last).uid).to eq('123')
expect(response).to redirect_to(root_path)
end
end

context 'with a matching user and a matching identity' do
before do
user = Fabricate(:user, email: '[email protected]')
Fabricate(:identity, user: user, uid: '123', provider: provider)
end

it 'matches the existing records and redirects to root path' do
expect { subject }
.to not_change(User, :count)
.and not_change(Identity, :count)
.and change(LoginActivity, :count)
.by(1)

expect(response).to redirect_to(root_path)
end
end
end

context 'with a response missing email address' do
before do
mock_omniauth(provider, {
provider: provider.to_s,
uid: '123',
info: {
verified: 'true',
},
})
end

it 'redirects to the auth setup page' do
expect { subject }
.to change(User, :count)
.by(1)
.and change(Identity, :count)
.by(1)
.and change(LoginActivity, :count)
.by(1)

expect(response).to redirect_to(auth_setup_path(missing_email: '1'))
end
end

context 'when a user cannot be built' do
before do
allow(User).to receive(:find_for_oauth).and_return(User.new)
end

it 'redirects to the new user signup page' do
expect { subject }
.to not_change(User, :count)
.and not_change(Identity, :count)
.and not_change(LoginActivity, :count)

expect(response).to redirect_to(new_user_registration_url)
end
end
end

describe '#openid_connect', if: ENV['OIDC_ENABLED'] == 'true' && ENV['OIDC_SCOPE'].present? do
include_examples 'omniauth provider callbacks', :openid_connect
end

describe '#cas', if: ENV['CAS_ENABLED'] == 'true' do
include_examples 'omniauth provider callbacks', :cas
end

describe '#saml', if: ENV['SAML_ENABLED'] == 'true' do
include_examples 'omniauth provider callbacks', :saml
end
end
7 changes: 7 additions & 0 deletions spec/support/omniauth_mocks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

OmniAuth.config.test_mode = true

def mock_omniauth(provider, data)
OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new(data)
end

0 comments on commit 50ff3d3

Please sign in to comment.