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
5 changes: 1 addition & 4 deletions lib/sorcery/controller/submodules/activity_logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ def self.included(base)
base.send(:include, InstanceMethods)
Config.module_eval do
class << self
attr_accessor :register_login_time
attr_accessor :register_logout_time
attr_accessor :register_last_activity_time
attr_accessor :register_last_ip_address
attr_accessor :register_login_time, :register_logout_time, :register_last_activity_time, :register_last_ip_address

def merge_activity_logging_defaults!
@defaults.merge!(:@register_login_time => true,
Expand Down
4 changes: 1 addition & 3 deletions lib/sorcery/test_helpers/internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def cost
# a patch to fix a bug in testing that happens when you 'destroy' a session twice.
# After the first destroy, the session is an ordinary hash, and then when destroy
# is called again there's an exception.
class ::Hash # rubocop:disable Style/ClassAndModuleChildren
class ::Hash
def destroy
clear
end
Expand Down Expand Up @@ -70,8 +70,6 @@ def reload_user_class
User && Object.send(:remove_const, 'User')
load 'user.rb'

return unless User.respond_to?(:reset_column_information)

User.reset_column_information
end
end
Expand Down
13 changes: 5 additions & 8 deletions lib/sorcery/test_helpers/internal/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,23 @@ def sorcery_reload!(submodules = [], options = {})

# remove all plugin before_actions so they won't fail other tests.
# I don't like this way, but I didn't find another.
chain = SorceryController._process_action_callbacks.send :chain
callbacks = SorceryController._process_action_callbacks
chain = callbacks.send :chain
chain.delete_if { |c| SUBMODULES_AUTO_ADDED_CONTROLLER_FILTERS.include?(c.filter) }
callbacks.instance_variable_set(:@all_callbacks, nil)
callbacks.instance_variable_set(:@single_callbacks, {})

# configure
::Sorcery::Controller::Config.submodules = submodules
::Sorcery::Controller::Config.user_class = nil
ActionController::Base.send(:include, ::Sorcery::Controller)
::Sorcery::Controller::Config.user_class = 'User'
ActionController::Base.include(::Sorcery::Controller)

::Sorcery::Controller::Config.user_config do |user|
options.each do |property, value|
user.send(:"#{property}=", value)
end
end
User.authenticates_with_sorcery!
return unless defined?(DataMapper) && User.ancestors.include?(DataMapper::Resource)

DataMapper.auto_migrate!
User.finalize
Authentication.finalize
end

def sorcery_controller_property_set(property, value)
Expand Down
56 changes: 31 additions & 25 deletions spec/controllers/controller_activity_logging_spec.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
require 'spec_helper'

# require 'shared_examples/controller_activity_logging_shared_examples'

describe SorceryController, type: :controller do
before(:all) do
MigrationHelper.migrate("#{Rails.root}/db/migrate/activity_logging")
end

after(:all) do
MigrationHelper.rollback("#{Rails.root}/db/migrate/activity_logging")
sorcery_controller_property_set(:register_login_time, true)
sorcery_controller_property_set(:register_logout_time, true)
sorcery_controller_property_set(:register_last_activity_time, true)
# sorcery_controller_property_set(:last_login_from_ip_address_name, true)
end

# ----------------- ACTIVITY LOGGING -----------------------
context 'with activity logging features' do
let(:adapter) { double('sorcery_adapter') }
let(:user) { double('user', id: 42, sorcery_adapter: adapter) }
let!(:user) { User.create!(username: 'test_user', email: '[email protected]', password: 'password') }

before(:all) do
sorcery_reload!([:activity_logging])
end
before(:all) { sorcery_reload!([:activity_logging]) }

before(:each) do
allow(user).to receive(:username)
allow(user).to receive_message_chain(:sorcery_config, :username_attribute_names, :first) { :username }
allow(User.sorcery_config).to receive(:last_login_at_attribute_name) { :last_login_at }
allow(User.sorcery_config).to receive(:last_login_from_ip_address_name) { :last_login_from_ip_address }

sorcery_controller_property_set(:register_login_time, false)
sorcery_controller_property_set(:register_last_ip_address, false)
sorcery_controller_property_set(:register_last_activity_time, false)
Expand All @@ -35,20 +29,22 @@
Timecop.freeze(now)

sorcery_controller_property_set(:register_login_time, true)
expect(user).to receive(:set_last_login_at).with(be_within(0.1).of(now))
login_user(user)

expect(user.reload.last_login_at).to be_within(0.1).of(now)

Timecop.return
end

it 'logs logout time on logout' do
login_user(user)
now = Time.now.in_time_zone
Timecop.freeze(now)
expect(user).to receive(:set_last_logout_at).with(be_within(0.1).of(now))

logout_user

expect(user.reload.last_logout_at).to be_within(0.1).of(now)

Timecop.return
end

Expand All @@ -58,56 +54,66 @@
login_user(user)
now = Time.now.in_time_zone
Timecop.freeze(now)
expect(user).to receive(:set_last_activity_at).with(be_within(0.1).of(now))

get :some_action

expect(user.reload.last_activity_at).to be_within(0.1).of(now)

Timecop.return
end

it 'logs last IP address when logged in' do
sorcery_controller_property_set(:register_last_ip_address, true)
expect(user).to receive(:set_last_ip_address).with('0.0.0.0')

login_user(user)

expect(user.reload.last_login_from_ip_address).to eq('0.0.0.0')
end

it 'updates nothing but activity fields' do
pending 'Move to model'
original_user_name = User.last.username
sorcery_controller_property_set(:register_last_activity_time, true)
user = User.last
original_email = user.email
original_activity_at = user.last_activity_at
login_user(user)
get :some_action_making_a_non_persisted_change_to_the_user

expect(User.last.username).to eq original_user_name
user.reload
expect(user.email).to eq original_email
expect(user.last_activity_at).not_to eq original_activity_at
end

it 'does not register login time if configured so' do
sorcery_controller_property_set(:register_login_time, false)

expect(user).to receive(:set_last_login_at).never
login_user(user)

expect(user.reload.last_login_at).to be_nil
end

it 'does not register logout time if configured so' do
sorcery_controller_property_set(:register_logout_time, false)
login_user(user)

expect(user).to receive(:set_last_logout_at).never
logout_user

expect(user.reload.last_logout_at).to be_nil
end

it 'does not register last activity time if configured so' do
sorcery_controller_property_set(:register_last_activity_time, false)

expect(user).to receive(:set_last_activity_at).never
login_user(user)
get :some_action

expect(user.reload.last_activity_at).to be_nil
end

it 'does not register last IP address if configured so' do
sorcery_controller_property_set(:register_last_ip_address, false)
expect(user).to receive(:set_last_ip_address).never

login_user(user)

expect(user.reload.last_login_from_ip_address).to be_nil
end
end
end
19 changes: 10 additions & 9 deletions spec/controllers/controller_brute_force_protection_spec.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
require 'spec_helper'

describe SorceryController, type: :controller do
let(:user) { double('user', id: 42, email: '[email protected]') }
let!(:user) { User.create!(username: 'test_user', email: '[email protected]', password: 'password') }

def request_test_login
get :test_login, params: { email: '[email protected]', password: 'blabla' }
end

# ----------------- SESSION TIMEOUT -----------------------
# ----------------- BRUTE FORCE PROTECTION -----------------------
describe 'brute force protection features' do
before(:all) do
MigrationHelper.migrate("#{Rails.root}/db/migrate/brute_force_protection")
sorcery_reload!([:brute_force_protection])
end

after(:each) do
Sorcery::Controller::Config.reset!
sorcery_controller_property_set(:user_class, User)
Timecop.return
after(:all) do
MigrationHelper.rollback("#{Rails.root}/db/migrate/brute_force_protection")
end

it 'counts login retries' do
Expand All @@ -29,13 +28,15 @@ def request_test_login
end

it 'resets the counter on a good login' do
# dirty hack for rails 4
allow(@controller).to receive(:register_last_activity_time_to_db)
# Set failed_logins_count to a non-zero value first
user.update!(failed_logins_count: 3)

allow(User).to receive(:authenticate) { |&block| block.call(user, nil) }
expect(user).to receive_message_chain(:sorcery_adapter, :update_attribute).with(:failed_logins_count, 0)

get :test_login, params: { email: '[email protected]', password: 'secret' }

user.reload
expect(user.failed_logins_count).to eq(0)
end
end
end
10 changes: 2 additions & 8 deletions spec/controllers/controller_http_basic_auth_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'spec_helper'

describe SorceryController, type: :controller do
let(:user) { double('user', id: 42, email: '[email protected]') }
let!(:user) { User.create!(username: 'test_user', email: '[email protected]', password: 'password') }

describe 'with http basic auth features' do
before(:all) do
Expand All @@ -21,9 +21,6 @@
end

it 'authenticates from http basic if credentials are sent' do
# dirty hack for rails 4
allow(subject).to receive(:register_last_activity_time_to_db)

@request.env['HTTP_AUTHORIZATION'] = "Basic #{Base64.encode64("#{user.email}:secret")}"
expect(User).to receive('authenticate').with('[email protected]', 'secret').and_return(user)
get :test_http_basic_auth, params: {}, session: { http_authentication_used: true }
Expand Down Expand Up @@ -53,15 +50,12 @@
end

it "signs in the user's session on successful login" do
# dirty hack for rails 4
allow(controller).to receive(:register_last_activity_time_to_db)

@request.env['HTTP_AUTHORIZATION'] = "Basic #{Base64.encode64("#{user.email}:secret")}"
expect(User).to receive('authenticate').with('[email protected]', 'secret').and_return(user)

get :test_http_basic_auth, params: {}, session: { http_authentication_used: true }

expect(session[:user_id]).to eq '42'
expect(session[:user_id]).to eq user.id.to_s
end
end
end
Loading