authie 3.3.2 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/db/migrate/20141012174250_create_authie_sessions.rb +4 -2
- data/db/migrate/20141013115205_add_indexes_to_authie_sessions.rb +4 -2
- data/db/migrate/20150109144120_add_parent_id_to_authie_sessions.rb +2 -0
- data/db/migrate/20150305135400_add_two_factor_auth_fields_to_authie.rb +3 -1
- data/db/migrate/20170417170000_add_token_hashes_to_authie_sessions.rb +2 -0
- data/db/migrate/20170421174100_add_index_to_token_hashes_on_authie_sessions.rb +3 -1
- data/db/migrate/20180215152200_add_host_to_authie_sessions.rb +2 -0
- data/lib/authie.rb +3 -3
- data/lib/authie/config.rb +4 -7
- data/lib/authie/controller_delegate.rb +14 -17
- data/lib/authie/controller_extension.rb +2 -2
- data/lib/authie/engine.rb +3 -3
- data/lib/authie/error.rb +2 -0
- data/lib/authie/event_manager.rb +11 -9
- data/lib/authie/rack_controller.rb +6 -10
- data/lib/authie/session.rb +103 -102
- data/lib/authie/user.rb +3 -3
- data/lib/authie/version.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3bbd79539a591d214d378e2f70f91ffb144fd6c0dfb31c36a4c0ea3d86bf1a5
|
4
|
+
data.tar.gz: 2ba2f38bc671db30ccbde3a6eda511aa642d7d9191dbb2d4311c890ff720249c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b325154016b3844263b77a71c484158ee256ab1ff4a67b77f670d167beff464805ab4b8a654931205124389c612a22723133f46f5b0123b9ad7102343c96b50
|
7
|
+
data.tar.gz: 9925f9aec3113b474b2a857676c163f570531fa5a5958d272a715fd448b7b5d2de8a1698ca23547023d8d01614009ca04035c11fc9d98ced2f83a1a228b2eaf9
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class CreateAuthieSessions < ActiveRecord::Migration[4.2]
|
2
4
|
def change
|
3
5
|
create_table :authie_sessions do |t|
|
4
6
|
t.string :token, :browser_id
|
5
7
|
t.integer :user_id
|
6
|
-
t.boolean :active, :
|
8
|
+
t.boolean :active, default: true
|
7
9
|
t.text :data
|
8
10
|
t.datetime :expires_at
|
9
11
|
t.datetime :login_at
|
@@ -11,7 +13,7 @@ class CreateAuthieSessions < ActiveRecord::Migration[4.2]
|
|
11
13
|
t.datetime :last_activity_at
|
12
14
|
t.string :last_activity_ip, :last_activity_path
|
13
15
|
t.string :user_agent
|
14
|
-
t.timestamps :
|
16
|
+
t.timestamps null: true
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class AddIndexesToAuthieSessions < ActiveRecord::Migration[4.2]
|
2
4
|
def change
|
3
5
|
add_column :authie_sessions, :user_type, :string
|
4
|
-
add_index :authie_sessions, :token, :
|
5
|
-
add_index :authie_sessions, :browser_id, :
|
6
|
+
add_index :authie_sessions, :token, length: 10
|
7
|
+
add_index :authie_sessions, :browser_id, length: 10
|
6
8
|
add_index :authie_sessions, :user_id
|
7
9
|
end
|
8
10
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class AddTwoFactorAuthFieldsToAuthie < ActiveRecord::Migration[4.2]
|
2
4
|
def change
|
3
5
|
add_column :authie_sessions, :two_factored_at, :datetime
|
4
6
|
add_column :authie_sessions, :two_factored_ip, :string
|
5
|
-
add_column :authie_sessions, :requests, :integer, :
|
7
|
+
add_column :authie_sessions, :requests, :integer, default: 0
|
6
8
|
add_column :authie_sessions, :password_seen_at, :datetime
|
7
9
|
end
|
8
10
|
end
|
data/lib/authie.rb
CHANGED
data/lib/authie/config.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'authie/event_manager'
|
2
4
|
|
3
5
|
module Authie
|
4
6
|
class Config
|
5
|
-
|
6
7
|
def initialize
|
7
8
|
@callbacks = {}
|
8
9
|
end
|
@@ -10,17 +11,15 @@ module Authie
|
|
10
11
|
def session_inactivity_timeout
|
11
12
|
@session_inactivity_timeout || 12.hours
|
12
13
|
end
|
13
|
-
attr_writer :session_inactivity_timeout
|
14
|
+
attr_writer :session_inactivity_timeout, :persistent_session_length, :sudo_session_timeout, :browser_id_cookie_name
|
14
15
|
|
15
16
|
def persistent_session_length
|
16
17
|
@persistent_session_length || 2.months
|
17
18
|
end
|
18
|
-
attr_writer :persistent_session_length
|
19
19
|
|
20
20
|
def sudo_session_timeout
|
21
21
|
@sudo_session_timeout || 10.minutes
|
22
22
|
end
|
23
|
-
attr_writer :sudo_session_timeout
|
24
23
|
|
25
24
|
def user_relationship_options
|
26
25
|
@user_relationship_options ||= {}
|
@@ -29,10 +28,9 @@ module Authie
|
|
29
28
|
def browser_id_cookie_name
|
30
29
|
@browser_id_cookie_name || :browser_id
|
31
30
|
end
|
32
|
-
attr_writer :browser_id_cookie_name
|
33
31
|
|
34
32
|
def events
|
35
|
-
@
|
33
|
+
@events ||= EventManager.new
|
36
34
|
end
|
37
35
|
end
|
38
36
|
|
@@ -44,5 +42,4 @@ module Authie
|
|
44
42
|
block.call(config)
|
45
43
|
config
|
46
44
|
end
|
47
|
-
|
48
45
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'securerandom'
|
2
4
|
require 'authie/session'
|
3
5
|
|
4
6
|
module Authie
|
5
7
|
class ControllerDelegate
|
6
|
-
|
7
8
|
def initialize(controller)
|
8
9
|
@controller = controller
|
9
10
|
end
|
@@ -12,24 +13,22 @@ module Authie
|
|
12
13
|
def set_browser_id
|
13
14
|
until cookies[Authie.config.browser_id_cookie_name]
|
14
15
|
proposed_browser_id = SecureRandom.uuid
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
next if Authie::Session.where(browser_id: proposed_browser_id).exists?
|
17
|
+
|
18
|
+
cookies[Authie.config.browser_id_cookie_name] = {
|
19
|
+
value: proposed_browser_id,
|
20
|
+
expires: 5.years.from_now,
|
21
|
+
httponly: true,
|
22
|
+
secure: @controller.request.ssl?
|
23
|
+
}
|
24
|
+
# Dispatch an event when the browser ID is set.
|
25
|
+
Authie.config.events.dispatch(:set_browser_id, proposed_browser_id)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
# Touch the auth session on each request if logged in
|
29
30
|
def touch_auth_session
|
30
|
-
if logged_in?
|
31
|
-
auth_session.touch!
|
32
|
-
end
|
31
|
+
auth_session.touch! if logged_in?
|
33
32
|
end
|
34
33
|
|
35
34
|
# Return the currently logged in user object
|
@@ -40,13 +39,12 @@ module Authie
|
|
40
39
|
# Set the currently logged in user
|
41
40
|
def current_user=(user)
|
42
41
|
create_auth_session(user)
|
43
|
-
user
|
44
42
|
end
|
45
43
|
|
46
44
|
# Create a new session for the given user
|
47
45
|
def create_auth_session(user)
|
48
46
|
if user
|
49
|
-
@auth_session = Authie::Session.start(@controller, :
|
47
|
+
@auth_session = Authie::Session.start(@controller, user: user)
|
50
48
|
else
|
51
49
|
auth_session.invalidate! if logged_in?
|
52
50
|
@auth_session = :none
|
@@ -81,6 +79,5 @@ module Authie
|
|
81
79
|
def cookies
|
82
80
|
@controller.send(:cookies)
|
83
81
|
end
|
84
|
-
|
85
82
|
end
|
86
83
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'authie/controller_delegate'
|
2
4
|
|
3
5
|
module Authie
|
4
6
|
module ControllerExtension
|
5
|
-
|
6
7
|
def self.included(base)
|
7
8
|
base.helper_method :logged_in?, :current_user, :auth_session
|
8
9
|
before_action_method = base.respond_to?(:before_action) ? :before_action : :before_filter
|
@@ -46,6 +47,5 @@ module Authie
|
|
46
47
|
def auth_session
|
47
48
|
auth_session_delegate.auth_session
|
48
49
|
end
|
49
|
-
|
50
50
|
end
|
51
51
|
end
|
data/lib/authie/engine.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Authie
|
2
4
|
class Engine < ::Rails::Engine
|
3
|
-
|
4
5
|
engine_name 'authie'
|
5
6
|
|
6
|
-
initializer 'authie.initialize' do |
|
7
|
+
initializer 'authie.initialize' do |_app|
|
7
8
|
ActiveSupport.on_load :active_record do
|
8
9
|
require 'authie/session'
|
9
10
|
end
|
@@ -13,6 +14,5 @@ module Authie
|
|
13
14
|
include Authie::ControllerExtension
|
14
15
|
end
|
15
16
|
end
|
16
|
-
|
17
17
|
end
|
18
18
|
end
|
data/lib/authie/error.rb
CHANGED
data/lib/authie/event_manager.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Authie
|
2
4
|
class EventManager
|
3
|
-
|
4
5
|
def initialize
|
5
6
|
@callbacks = {}
|
6
7
|
end
|
7
8
|
|
8
9
|
def dispatch(event, *args)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
callbacks = @callbacks[event.to_sym]
|
11
|
+
return if callbacks.nil?
|
12
|
+
|
13
|
+
callbacks.each do |cb|
|
14
|
+
cb.call(*args)
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -19,10 +21,10 @@ module Authie
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def remove(event, block)
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
24
|
+
cb = @callbacks[event.to_sym]
|
25
|
+
return if cb.nil?
|
26
26
|
|
27
|
+
cb.delete(block)
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# If you're dealing with your authentication in a middleware and you only have
|
2
4
|
# access to your rack environment, this will wrap around rack and make it look
|
3
5
|
# close enough to an ActionController to work with Authie
|
@@ -9,7 +11,6 @@
|
|
9
11
|
|
10
12
|
module Authie
|
11
13
|
class RackController
|
12
|
-
|
13
14
|
attr_reader :request
|
14
15
|
|
15
16
|
def initialize(env)
|
@@ -26,27 +27,22 @@ module Authie
|
|
26
27
|
def set_browser_id
|
27
28
|
until cookies[:browser_id]
|
28
29
|
proposed_browser_id = SecureRandom.uuid
|
29
|
-
unless Session.where(:
|
30
|
-
cookies[:browser_id] = {:
|
30
|
+
unless Session.where(browser_id: proposed_browser_id).exists?
|
31
|
+
cookies[:browser_id] = { value: proposed_browser_id, expires: 20.years.from_now }
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
36
|
def current_user=(user)
|
36
|
-
Session.start(self, :
|
37
|
+
Session.start(self, user: user)
|
37
38
|
end
|
38
39
|
|
39
40
|
def current_user
|
40
|
-
if auth_session.is_a?(Session)
|
41
|
-
auth_session.user
|
42
|
-
else
|
43
|
-
nil
|
44
|
-
end
|
41
|
+
auth_session.user if auth_session.is_a?(Session)
|
45
42
|
end
|
46
43
|
|
47
44
|
def auth_session
|
48
45
|
@auth_session ||= Session.get_session(self)
|
49
46
|
end
|
50
|
-
|
51
47
|
end
|
52
48
|
end
|
data/lib/authie/session.rb
CHANGED
@@ -1,49 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'secure_random_string'
|
2
4
|
|
3
5
|
module Authie
|
4
6
|
class Session < ActiveRecord::Base
|
5
|
-
|
6
7
|
# Errors which will be raised when there's an issue with a session's
|
7
8
|
# validity in the request.
|
8
9
|
class ValidityError < Error; end
|
10
|
+
|
9
11
|
class InactiveSession < ValidityError; end
|
12
|
+
|
10
13
|
class ExpiredSession < ValidityError; end
|
14
|
+
|
11
15
|
class BrowserMismatch < ValidityError; end
|
16
|
+
|
12
17
|
class HostMismatch < ValidityError; end
|
13
18
|
|
14
19
|
class NoParentSessionForRevert < Error; end
|
15
20
|
|
16
21
|
# Set table name
|
17
|
-
self.table_name =
|
22
|
+
self.table_name = 'authie_sessions'
|
18
23
|
|
19
24
|
# Relationships
|
20
|
-
parent_options = {:
|
25
|
+
parent_options = { class_name: 'Authie::Session' }
|
21
26
|
parent_options[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
|
22
|
-
belongs_to :parent, parent_options
|
27
|
+
belongs_to :parent, **parent_options
|
23
28
|
|
24
29
|
# Scopes
|
25
|
-
scope :active, -> { where(:
|
26
|
-
scope :asc, -> { order(:
|
27
|
-
scope :for_user, ->
|
30
|
+
scope :active, -> { where(active: true) }
|
31
|
+
scope :asc, -> { order(last_activity_at: :desc) }
|
32
|
+
scope :for_user, ->(user) { where(user_type: user.class.name, user_id: user.id) }
|
28
33
|
|
29
34
|
# Attributes
|
30
35
|
serialize :data, Hash
|
31
|
-
attr_accessor :controller
|
32
|
-
attr_accessor :temporary_token
|
36
|
+
attr_accessor :controller, :temporary_token
|
33
37
|
|
34
38
|
before_validation do
|
35
|
-
|
36
|
-
self.user_agent = self.user_agent[0,255]
|
37
|
-
end
|
39
|
+
self.user_agent = user_agent[0, 255] if user_agent.is_a?(String)
|
38
40
|
|
39
|
-
|
40
|
-
self.last_activity_path = self.last_activity_path[0,255]
|
41
|
-
end
|
41
|
+
self.last_activity_path = last_activity_path[0, 255] if last_activity_path.is_a?(String)
|
42
42
|
end
|
43
43
|
|
44
44
|
before_create do
|
45
45
|
self.temporary_token = SecureRandomString.new(44)
|
46
|
-
self.token_hash = self.class.hash_token(
|
46
|
+
self.token_hash = self.class.hash_token(temporary_token)
|
47
47
|
if controller
|
48
48
|
self.user_agent = controller.request.user_agent
|
49
49
|
set_cookie!
|
@@ -56,10 +56,10 @@ module Authie
|
|
56
56
|
|
57
57
|
# Return the user that
|
58
58
|
def user
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
return unless user_id && user_type
|
60
|
+
|
61
|
+
@user ||= user_type.constantize.find_by(id: user_id) || :none
|
62
|
+
@user == :none ? nil : @user
|
63
63
|
end
|
64
64
|
|
65
65
|
# Set the user
|
@@ -76,35 +76,37 @@ module Authie
|
|
76
76
|
# This method should be called each time a user performs an
|
77
77
|
# action while authenticated with this session.
|
78
78
|
def touch!
|
79
|
-
|
79
|
+
check_security!
|
80
80
|
self.last_activity_at = Time.now
|
81
81
|
self.last_activity_ip = controller.request.ip
|
82
82
|
self.last_activity_path = controller.request.path
|
83
83
|
self.requests += 1
|
84
|
-
|
84
|
+
save!
|
85
85
|
Authie.config.events.dispatch(:session_touched, self)
|
86
86
|
true
|
87
87
|
end
|
88
88
|
|
89
89
|
# Sets the cookie on the associated controller.
|
90
|
-
|
90
|
+
# rubocop:disable Naming/AccessorMethodName
|
91
|
+
def set_cookie!(value = temporary_token)
|
91
92
|
cookies[:user_session] = {
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
93
|
+
value: value,
|
94
|
+
secure: controller.request.ssl?,
|
95
|
+
httponly: true,
|
96
|
+
expires: expires_at
|
96
97
|
}
|
97
98
|
Authie.config.events.dispatch(:session_cookie_updated, self)
|
98
99
|
true
|
99
100
|
end
|
101
|
+
# rubocop:enable Naming/AccessorMethodName
|
100
102
|
|
101
103
|
# Sets the cookie for the parent session on the associated controller.
|
102
104
|
def set_parent_cookie!
|
103
105
|
cookies[:parent_user_session] = {
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
:
|
106
|
+
value: cookies[:user_session],
|
107
|
+
secure: controller.request.ssl?,
|
108
|
+
httponly: true,
|
109
|
+
expires: expires_at
|
108
110
|
}
|
109
111
|
Authie.config.events.dispatch(:parent_session_cookie_updated, self)
|
110
112
|
true
|
@@ -112,57 +114,59 @@ module Authie
|
|
112
114
|
|
113
115
|
# Check the security of the session to ensure it can be used.
|
114
116
|
def check_security!
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
117
|
+
raise Authie::Error, 'Cannot check security without a controller' unless controller
|
118
|
+
|
119
|
+
if cookies[:browser_id] != browser_id
|
120
|
+
invalidate!
|
121
|
+
Authie.config.events.dispatch(:browser_id_mismatch_error, self)
|
122
|
+
raise BrowserMismatch, 'Browser ID mismatch'
|
123
|
+
end
|
124
|
+
|
125
|
+
unless active?
|
126
|
+
invalidate!
|
127
|
+
Authie.config.events.dispatch(:invalid_session_error, self)
|
128
|
+
raise InactiveSession, 'Session is no longer active'
|
129
|
+
end
|
130
|
+
|
131
|
+
if expired?
|
132
|
+
invalidate!
|
133
|
+
Authie.config.events.dispatch(:expired_session_error, self)
|
134
|
+
raise ExpiredSession, 'Persistent session has expired'
|
135
|
+
end
|
136
|
+
|
137
|
+
if inactive?
|
138
|
+
invalidate!
|
139
|
+
Authie.config.events.dispatch(:inactive_session_error, self)
|
140
|
+
raise InactiveSession, 'Non-persistent session has expired'
|
141
|
+
end
|
142
|
+
|
143
|
+
if host && host != controller.request.host
|
144
|
+
invalidate!
|
145
|
+
Authie.config.events.dispatch(:host_mismatch_error, self)
|
146
|
+
raise HostMismatch, "Session was created on #{host} but accessed using #{controller.request.host}"
|
145
147
|
end
|
148
|
+
|
149
|
+
true
|
146
150
|
end
|
147
151
|
|
148
152
|
# Has this persistent session expired?
|
149
153
|
def expired?
|
150
|
-
|
151
|
-
|
154
|
+
expires_at &&
|
155
|
+
expires_at < Time.now
|
152
156
|
end
|
153
157
|
|
154
158
|
# Has a non-persistent session become inactive?
|
155
159
|
def inactive?
|
156
|
-
|
157
|
-
|
158
|
-
|
160
|
+
expires_at.nil? &&
|
161
|
+
last_activity_at &&
|
162
|
+
last_activity_at < Authie.config.session_inactivity_timeout.ago
|
159
163
|
end
|
160
164
|
|
161
165
|
# Allow this session to persist rather than expiring at the end of the
|
162
166
|
# current browser session
|
163
167
|
def persist!
|
164
168
|
self.expires_at = Authie.config.persistent_session_length.from_now
|
165
|
-
|
169
|
+
save!
|
166
170
|
set_cookie!
|
167
171
|
end
|
168
172
|
|
@@ -174,16 +178,14 @@ module Authie
|
|
174
178
|
# Activate an old session
|
175
179
|
def activate!
|
176
180
|
self.active = true
|
177
|
-
|
181
|
+
save!
|
178
182
|
end
|
179
183
|
|
180
184
|
# Mark this session as invalid
|
181
185
|
def invalidate!
|
182
186
|
self.active = false
|
183
|
-
|
184
|
-
if controller
|
185
|
-
cookies.delete(:user_session)
|
186
|
-
end
|
187
|
+
save!
|
188
|
+
cookies.delete(:user_session) if controller
|
187
189
|
Authie.config.events.dispatch(:session_invalidated, self)
|
188
190
|
true
|
189
191
|
end
|
@@ -192,7 +194,7 @@ module Authie
|
|
192
194
|
def set(key, value)
|
193
195
|
self.data ||= {}
|
194
196
|
self.data[key.to_s] = value
|
195
|
-
|
197
|
+
save!
|
196
198
|
end
|
197
199
|
|
198
200
|
# Get some additional data from this session
|
@@ -202,34 +204,32 @@ module Authie
|
|
202
204
|
|
203
205
|
# Invalidate all sessions but this one for this user
|
204
206
|
def invalidate_others!
|
205
|
-
self.class.where(
|
206
|
-
s.invalidate!
|
207
|
-
end
|
207
|
+
self.class.where('id != ?', id).for_user(user).each(&:invalidate!)
|
208
208
|
end
|
209
209
|
|
210
210
|
# Note that we have just seen the user enter their password.
|
211
211
|
def see_password!
|
212
212
|
self.password_seen_at = Time.now
|
213
|
-
|
213
|
+
save!
|
214
214
|
Authie.config.events.dispatch(:seen_password, self)
|
215
215
|
true
|
216
216
|
end
|
217
217
|
|
218
218
|
# Have we seen the user's password recently in this sesion?
|
219
219
|
def recently_seen_password?
|
220
|
-
!!(
|
220
|
+
!!(password_seen_at && password_seen_at >= Authie.config.sudo_session_timeout.ago)
|
221
221
|
end
|
222
222
|
|
223
223
|
# Is two factor authentication required for this request?
|
224
224
|
def two_factored?
|
225
|
-
!!(two_factored_at ||
|
225
|
+
!!(two_factored_at || parent_id)
|
226
226
|
end
|
227
227
|
|
228
228
|
# Mark this request as two factor authoritsed
|
229
229
|
def mark_as_two_factored!
|
230
230
|
self.two_factored_at = Time.now
|
231
231
|
self.two_factored_ip = controller.request.ip
|
232
|
-
|
232
|
+
save!
|
233
233
|
Authie.config.events.dispatch(:marked_as_two_factored, self)
|
234
234
|
true
|
235
235
|
end
|
@@ -237,38 +237,38 @@ module Authie
|
|
237
237
|
# Create a new session for impersonating for the given user
|
238
238
|
def impersonate!(user)
|
239
239
|
set_parent_cookie!
|
240
|
-
self.class.start(controller, :
|
240
|
+
self.class.start(controller, user: user, parent: self)
|
241
241
|
end
|
242
242
|
|
243
243
|
# Revert back to the parent session
|
244
244
|
def revert_to_parent!
|
245
|
-
|
246
|
-
|
247
|
-
self.parent.activate!
|
248
|
-
self.parent.controller = self.controller
|
249
|
-
self.parent.set_cookie!(cookies[:parent_user_session])
|
250
|
-
cookies.delete(:parent_user_session)
|
251
|
-
self.parent
|
252
|
-
else
|
253
|
-
raise NoParentSessionForRevert, "Session does not have a parent therefore cannot be reverted."
|
245
|
+
unless parent && cookies[:parent_user_session]
|
246
|
+
raise NoParentSessionForRevert, 'Session does not have a parent therefore cannot be reverted.'
|
254
247
|
end
|
248
|
+
|
249
|
+
invalidate!
|
250
|
+
parent.activate!
|
251
|
+
parent.controller = controller
|
252
|
+
parent.set_cookie!(cookies[:parent_user_session])
|
253
|
+
cookies.delete(:parent_user_session)
|
254
|
+
parent
|
255
255
|
end
|
256
256
|
|
257
257
|
# Is this the first session for this session's browser?
|
258
258
|
def first_session_for_browser?
|
259
|
-
self.class.where(
|
259
|
+
self.class.where('id < ?', id).for_user(user).where(browser_id: browser_id).empty?
|
260
260
|
end
|
261
261
|
|
262
262
|
# Is this the first session for the IP?
|
263
263
|
def first_session_for_ip?
|
264
|
-
self.class.where(
|
264
|
+
self.class.where('id < ?', id).for_user(user).where(login_ip: login_ip).empty?
|
265
265
|
end
|
266
266
|
|
267
267
|
# Find a session from the database for the given controller instance.
|
268
268
|
# Returns a session object or :none if no session is found.
|
269
269
|
def self.get_session(controller)
|
270
270
|
cookies = controller.send(:cookies)
|
271
|
-
if cookies[:user_session] && session =
|
271
|
+
if cookies[:user_session] && (session = find_session_by_token(cookies[:user_session]))
|
272
272
|
session.temporary_token = cookies[:user_session]
|
273
273
|
session.controller = controller
|
274
274
|
session
|
@@ -280,17 +280,18 @@ module Authie
|
|
280
280
|
# Find a session by a token (either from a hash or from the raw token)
|
281
281
|
def self.find_session_by_token(token)
|
282
282
|
return nil if token.blank?
|
283
|
-
|
283
|
+
|
284
|
+
active.where('token = ? OR token_hash = ?', token, hash_token(token)).first
|
284
285
|
end
|
285
286
|
|
286
287
|
# Create a new session and return the newly created session object.
|
287
288
|
# Any other sessions for the browser will be invalidated.
|
288
289
|
def self.start(controller, params = {})
|
289
290
|
cookies = controller.send(:cookies)
|
290
|
-
|
291
|
+
active.where(browser_id: cookies[:browser_id]).each(&:invalidate!)
|
291
292
|
user_object = params.delete(:user)
|
292
293
|
|
293
|
-
session =
|
294
|
+
session = new(params)
|
294
295
|
session.user = user_object
|
295
296
|
session.controller = controller
|
296
297
|
session.browser_id = cookies[:browser_id]
|
@@ -306,9 +307,10 @@ module Authie
|
|
306
307
|
def self.cleanup
|
307
308
|
Authie.config.events.dispatch(:before_cleanup)
|
308
309
|
# Invalidate transient sessions that haven't been used
|
309
|
-
|
310
|
+
active.where('expires_at IS NULL AND last_activity_at < ?',
|
311
|
+
Authie.config.session_inactivity_timeout.ago).each(&:invalidate!)
|
310
312
|
# Invalidate persistent sessions that have expired
|
311
|
-
|
313
|
+
active.where('expires_at IS NOT NULL AND expires_at < ?', Time.now).each(&:invalidate!)
|
312
314
|
Authie.config.events.dispatch(:after_cleanup)
|
313
315
|
true
|
314
316
|
end
|
@@ -320,9 +322,9 @@ module Authie
|
|
320
322
|
|
321
323
|
# Convert all existing active sessions to store their tokens in the database
|
322
324
|
def self.convert_tokens_to_hashes
|
323
|
-
active.where(:
|
324
|
-
hash =
|
325
|
-
|
325
|
+
active.where(token_hash: nil).where('token is not null').each do |s|
|
326
|
+
hash = hash_token(s.token)
|
327
|
+
where(id: s.id).update_all(token_hash: hash, token: nil)
|
326
328
|
end
|
327
329
|
end
|
328
330
|
|
@@ -332,6 +334,5 @@ module Authie
|
|
332
334
|
def cookies
|
333
335
|
controller.send(:cookies)
|
334
336
|
end
|
335
|
-
|
336
337
|
end
|
337
338
|
end
|
data/lib/authie/user.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Authie
|
2
4
|
module User
|
3
|
-
|
4
5
|
def self.included(base)
|
5
|
-
base.has_many :user_sessions, :
|
6
|
+
base.has_many :user_sessions, class_name: 'Authie::Session', as: :user, dependent: :delete_all
|
6
7
|
end
|
7
|
-
|
8
8
|
end
|
9
9
|
end
|
data/lib/authie/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Cooke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: secure_random_string
|