Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: ['3.3', '3.4']
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- run: bundle exec rake test
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby 3.2.2
ruby 3.4.8
3 changes: 1 addition & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ group :test do
gem 'actionpack', '~> 8.0'
gem 'actionmailer', '~> 8.0'
gem "sqlite3", "~> 2.0"
gem "bson_ext", "~> 1.3"
gem "capybara", "~> 3.39"
gem 'shoulda', '~> 2.11.3'
gem 'mocha', '~> 1.14.0'
gem 'mocha', '~> 2.0'
gem 'factory_bot_rails' # Updated from factory_girl_rails
gem 'timecop'
gem 'test-unit'
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/devise/checkga_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def update
resource = resource_class.find_by_gauth_tmp(params[resource_name]['tmpid'])

fail ErrorSigningIn unless resource
fail ErrorSigningIn unless resource.validate_token(params[resource_name]['gauth_token'].to_i)
fail ErrorSigningIn unless resource.validate_token(params[resource_name]['gauth_token'])

set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name,resource)
Expand Down
22 changes: 4 additions & 18 deletions app/controllers/devise/displayqr_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@ class InvalidToken < StandardError; end

# GET /{resource}/displayqr
def show
if resource && resource.gauth_secret
if !resource.gauth_enabled? && resource.gauth_secret.blank?
resource.send(:assign_auth_secret)
resource.save
end
@tmpid = resource.assign_tmp
render :show
else
sign_in resource_class.new, resource
redirect_to stored_location_for(scope) || :root
end
@tmpid = resource.assign_tmp
render :show
end

def update
fail InvalidToken if resource.gauth_tmp != params[resource_name]['tmpid']
fail InvalidToken unless resource.validate_token(params[resource_name]['gauth_token'].to_i)
fail InvalidToken unless resource.validate_token(params[resource_name]['gauth_token'])

if resource.set_gauth_enabled(params[resource_name]['gauth_enabled'])
set_flash_message :notice, (resource.gauth_enabled? ? :enabled : :disabled)
Expand Down Expand Up @@ -59,12 +50,7 @@ def authenticate_scope!
self.resource = send("current_#{resource_name}")
end

# 7/2/15 - Unsure if this is used anymore - @xntrik
def resource_params
if defined?(ActionController::StrongParameters)
params.require(resource_name.to_sym).permit(:gauth_enabled)
else
params
end
params.require(resource_name.to_sym).permit(:gauth_enabled)
end
end
3 changes: 1 addition & 2 deletions devise_google_authenticator.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 3.0.0'
# s.required_rubygems_version = '>= 2.1.0'

# Updated for Rails 8 compatibility
s.add_runtime_dependency 'devise', '>= 4.8.0', '< 6.0'
s.add_runtime_dependency 'devise', '~> 5.0'
s.add_runtime_dependency 'rotp', '>= 1.6'
s.add_runtime_dependency 'rqrcode', '>= 0.10.1'
end
4 changes: 1 addition & 3 deletions lib/devise_google_authenticatable/rails.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
module DeviseGoogleAuthenticator
class Engine < ::Rails::Engine # :nodoc:
# Load locale files
config.before_configuration do
I18n.load_path += Dir[Engine.root.join('config', 'locales', '*.yml')]
end

# Rails 5+ uses ActiveSupport::Reloader, older versions use ActionDispatch::Callbacks
(DeviseGoogleAuthenticator.rails5_or_newer? ? ActiveSupport::Reloader : ActionDispatch::Callbacks).to_prepare do
ActiveSupport::Reloader.to_prepare do
DeviseGoogleAuthenticator::Patches.apply
end
end
Expand Down
9 changes: 0 additions & 9 deletions lib/devise_google_authenticator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ module DeviseGoogleAuthenticator
autoload :Schema, 'devise_google_authenticatable/schema'
autoload :Patches, 'devise_google_authenticatable/patches'

class << self
def rails5?
Gem.loaded_specs['activesupport'].version >= Gem::Version.new('5.0.0.beta')
end

def rails5_or_newer?
rails5?
end
end
end

require 'devise_google_authenticatable/routes'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,12 @@ def inject_devise_google_authenticator_content

if File.exist?(path)
inject_into_file(path, "google_authenticatable, :", :after => "devise :")
inject_into_file(path, "gauth_enabled, :gauth_tmp, :gauth_tmp_datetime, :", :after => "attr_accessible :") if needs_attr_accessible?
inject_into_class(path, class_name, "\tattr_accessor :gauth_token\n")
end
end

hook_for :orm

private

def needs_attr_accessible?
rails_3? && !strong_parameters_enabled?
end

def rails_3?
Rails::VERSION::MAJOR == 3
end

def strong_parameters_enabled?
defined?(ActionController::StrongParameters)
end

end
end
end
126 changes: 42 additions & 84 deletions test/integration/gauth_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ class InvitationTest < ActionDispatch::IntegrationTest
include IntegrationTestHelpers
self.use_transactional_tests = true

FROZEN_TIME = Time.utc(2026, 1, 1, 12, 0, 5)

def setup
Timecop.freeze(FROZEN_TIME)
end

def teardown
Capybara.reset_sessions!
Timecop.return
# Restore default configuration values in case any test modified them
User.ga_timeout = 3.minutes
User.ga_timedrift = 3
User.ga_remembertime = 1.month
Expand All @@ -23,7 +28,6 @@ def teardown

assert_equal user_displayqr_path, current_path

# Get the user we just signed up's token
testuser = User.find_by_email("test@test.com")
fill_in('user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now))
click_button 'Continue...'
Expand All @@ -33,7 +37,7 @@ def teardown

test 'a new user should be able to sign in without using their token' do
create_full_user
User.find_by_email("fulluser@test.com").update(:gauth_enabled => 0) # force this off - unsure why sometimes it flicks on possible race condition
User.find_by_email("fulluser@test.com").update(:gauth_enabled => 0)

visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
Expand All @@ -43,9 +47,8 @@ def teardown
end

test 'a new user should be able to sign in and change their qr code to enabled' do
# sign_in_as_user
create_full_user
User.find_by_email("fulluser@test.com").update(:gauth_enabled => 0) # force this off - unsure why sometimes it flicks on possible race condition
User.find_by_email("fulluser@test.com").update(:gauth_enabled => 0)
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => '123456'
Expand All @@ -54,7 +57,6 @@ def teardown
visit user_displayqr_path

check 'user_gauth_enabled'
# Get the user we just signed up's token
testuser = User.find_by_email("fulluser@test.com")
fill_in('user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now))
click_button 'Continue...'
Expand All @@ -63,38 +65,21 @@ def teardown
end

test 'a new user should be able to sign in change their qr to enabled and be prompted for their token' do
create_full_user
User.find_by_email("fulluser@test.com").update(:gauth_enabled => 0) # force this off - unsure why sometimes it flicks on possible race condition
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => '123456'
click_button 'Log in'

visit user_displayqr_path
check 'user_gauth_enabled'
# Get the user we just signed up's token
testuser = User.find_by_email("fulluser@test.com")
fill_in('user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now))
click_button 'Continue...'

Capybara.reset_sessions!
testuser = create_full_user
testuser.update(:gauth_enabled => '1')

visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => '123456'
click_button 'Log in'

assert_equal user_checkga_path, current_path

end

test 'if resource is nil redirects back to custom url' do
testuser = create_full_user

visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => '123456'
click_button 'Log in'
User.stubs(:find_by_gauth_tmp).returns(nil)
Devise::CheckgaController.any_instance.stubs(:redirect_on_error_url).returns('/foo')
testuser = create_and_signin_gauth_user

fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now)
click_button 'Check Token'
Expand All @@ -112,26 +97,17 @@ def teardown
end

test 'fail token authentication redirects back to custom url' do
create_full_user
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => '123456'
click_button 'Log in'
Devise::CheckgaController.any_instance.stubs(:redirect_on_error_url).returns('/foo')
create_and_signin_gauth_user

fill_in 'user_gauth_token', :with => "wrong token"
click_button 'Check Token'
assert_equal foo_path, current_path
Capybara.reset_sessions!
end

test 'successfull token authentication' do
create_full_user
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => "123456"
click_button "Log in"
save_and_open_page
test 'successful token authentication' do
testuser = create_and_signin_gauth_user
fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now)
click_button 'Check Token'

Expand All @@ -140,63 +116,48 @@ def teardown
end

test 'unsuccessful login - if ga_timeout is short' do
create_full_user
old_ga_timeout = User.ga_timeout
User.ga_timeout = 1.second
begin
User.ga_timeout = 1.second

# testuser = create_and_signin_gauth_user
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => "123456"
click_button "Log in"
testuser = create_and_signin_gauth_user

sleep(5)
Timecop.freeze(FROZEN_TIME + 5)

fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now)
click_button 'Check Token'

User.ga_timeout = old_ga_timeout
fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now)
click_button 'Check Token'

assert_equal new_user_session_path, current_path
Capybara.reset_sessions!
assert_equal new_user_session_path, current_path
ensure
User.ga_timeout = old_ga_timeout
Capybara.reset_sessions!
end
end

test 'unsuccessful login - if ga_timedrift is short' do
create_full_user
old_ga_timedrift = User.ga_timedrift
User.ga_timedrift = 1

# testuser = create_and_signin_gauth_user
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => "123456"
click_button "Log in"
fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now.in(60))
click_button 'Check Token'

User.ga_timedrift = old_ga_timedrift

assert_equal new_user_session_path, current_path
Capybara.reset_sessions!
begin
User.ga_timedrift = 1

testuser = create_and_signin_gauth_user
fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now.in(60))
click_button 'Check Token'

assert_equal new_user_session_path, current_path
ensure
User.ga_timedrift = old_ga_timedrift
Capybara.reset_sessions!
end
end

test 'user is not prompted for token again after first login until remembertime is up' do
# testuser = create_and_signin_gauth_user
create_full_user
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => "123456"
click_button "Log in"
testuser = create_and_signin_gauth_user
fill_in 'user_gauth_token', :with => ROTP::TOTP.new(testuser.get_qr).at(Time.now)
click_button 'Check Token'

assert_equal root_path, current_path

visit destroy_user_session_path
# sign_in_as_user(testuser)
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
Expand All @@ -205,15 +166,12 @@ def teardown
assert_equal root_path, current_path
visit destroy_user_session_path

Timecop.travel(1.month.to_i + 1.day.to_i)
# sign_in_as_user(testuser)
Timecop.freeze(FROZEN_TIME + 1.month + 1.day)
testuser = User.find_by_email("fulluser@test.com")
visit new_user_session_path
fill_in 'user_email', :with => 'fulluser@test.com'
fill_in 'user_password', :with => "123456"
click_button "Log in"
assert_equal user_checkga_path, current_path

Timecop.return
end
end
Loading
Loading