Skip to content

Commit

Permalink
Add ability to skip session limitable on a per record basis (#74)
Browse files Browse the repository at this point in the history
* Add ability to skip session limitable on a per record basis by defining a `skip_session_limitable?` method on the model

Co-authored-by: Kevin Olbrich <kevin.olbrich@gmail.com>
  • Loading branch information
surjay and olbrich committed Jul 21, 2019
1 parent 7aa7d34 commit 37b7201
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
20 changes: 13 additions & 7 deletions lib/devise-security/hooks/session_limitable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# user is explicitly set (with set_user) and on authentication. Retrieving the
# user from session (:fetch) does not trigger it.
Warden::Manager.after_set_user except: :fetch do |record, warden, options|
if record.respond_to?(:update_unique_session_id!) && warden.authenticated?(options[:scope])
if record.devise_modules.include?(:session_limitable) &&
warden.authenticated?(options[:scope]) &&
!record.skip_session_limitable?
unique_session_id = Devise.friendly_token
warden.session(options[:scope])['unique_session_id'] = unique_session_id
record.update_unique_session_id!(unique_session_id)
Expand All @@ -19,13 +21,17 @@
scope = options[:scope]
env = warden.request.env

if record.respond_to?(:unique_session_id) && warden.authenticated?(scope) && options[:store] != false
if record.unique_session_id != warden.session(scope)['unique_session_id'] && !env['devise.skip_session_limitable']
Rails.logger.warn {
"[devise-security][session_limitable] session id mismatch: "\
if record.devise_modules.include?(:session_limitable) &&
warden.authenticated?(scope) &&
options[:store] != false
if record.unique_session_id != warden.session(scope)['unique_session_id'] &&
!env['devise.skip_session_limitable'] &&
!record.skip_session_limitable?
Rails.logger.warn do
'[devise-security][session_limitable] session id mismatch: '\
"expected=#{record.unique_session_id.inspect} "\
"actual=#{warden.session(scope)['unique_session_id'].inspect}"
}
"actual=#{warden.session(scope)['unique_session_id'].inspect}"
end
warden.raw_session.clear
warden.logout(scope)
throw :warden, scope: scope, message: :session_limited
Expand Down
9 changes: 8 additions & 1 deletion lib/devise-security/models/session_limitable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ module SessionLimitable
# @raise [Devise::Models::Compatibility::NotPersistedError] if record is unsaved
def update_unique_session_id!(unique_session_id)
raise Devise::Models::Compatibility::NotPersistedError, 'cannot update a new record' unless persisted?

update_attribute_without_validatons_or_callbacks(:unique_session_id, unique_session_id).tap do
Rails.logger.debug { "[devise-security][session_limitable] unique_session_id=#{unique_session_id}"}
Rails.logger.debug { "[devise-security][session_limitable] unique_session_id=#{unique_session_id}" }
end
end

# Should session_limitable be skipped for this instance?
# @return [Boolean]
# @return [false] by default. This can be overridden by application logic as necessary.
def skip_session_limitable?
false
end
end
end
end
17 changes: 17 additions & 0 deletions test/test_session_limitable.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# frozen_string_literal: true

require 'test_helper'

class TestSessionLimitable < ActiveSupport::TestCase
class ModifiedUser < User
def skip_session_limitable?
true
end
end

test 'check is not skipped by default' do
user = User.create email: 'bob@microsoft.com', password: 'password1', password_confirmation: 'password1'
assert_equal(false, user.skip_session_limitable?)
end

test 'default check can be overridden by record instance' do
modified_user = ModifiedUser.create email: 'bob2@microsoft.com', password: 'password1', password_confirmation: 'password1'
assert_equal(true, modified_user.skip_session_limitable?)
end

class SessionLimitableUser < User
devise :session_limitable
include ::Mongoid::Mappings if DEVISE_ORM == :mongoid
Expand Down

0 comments on commit 37b7201

Please sign in to comment.