authie 3.1.5 → 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 +36 -18
- data/lib/authie/controller_extension.rb +10 -2
- data/lib/authie/engine.rb +3 -4
- 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 +134 -94
- data/lib/authie/user.rb +3 -3
- data/lib/authie/version.rb +3 -1
- metadata +19 -33
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
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,6 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require 'authie/session'
|
5
|
+
|
1
6
|
module Authie
|
2
7
|
class ControllerDelegate
|
3
|
-
|
4
8
|
def initialize(controller)
|
5
9
|
@controller = controller
|
6
10
|
end
|
@@ -9,24 +13,22 @@ module Authie
|
|
9
13
|
def set_browser_id
|
10
14
|
until cookies[Authie.config.browser_id_cookie_name]
|
11
15
|
proposed_browser_id = SecureRandom.uuid
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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)
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
29
|
# Touch the auth session on each request if logged in
|
26
30
|
def touch_auth_session
|
27
|
-
if logged_in?
|
28
|
-
auth_session.touch!
|
29
|
-
end
|
31
|
+
auth_session.touch! if logged_in?
|
30
32
|
end
|
31
33
|
|
32
34
|
# Return the currently logged in user object
|
@@ -36,11 +38,27 @@ module Authie
|
|
36
38
|
|
37
39
|
# Set the currently logged in user
|
38
40
|
def current_user=(user)
|
41
|
+
create_auth_session(user)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create a new session for the given user
|
45
|
+
def create_auth_session(user)
|
39
46
|
if user
|
40
|
-
@auth_session = Session.start(@controller, :
|
47
|
+
@auth_session = Authie::Session.start(@controller, user: user)
|
41
48
|
else
|
42
49
|
auth_session.invalidate! if logged_in?
|
43
|
-
@auth_session =
|
50
|
+
@auth_session = :none
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Invalidate an existing auth session
|
55
|
+
def invalidate_auth_session
|
56
|
+
if logged_in?
|
57
|
+
auth_session.invalidate!
|
58
|
+
@auth_session = :none
|
59
|
+
true
|
60
|
+
else
|
61
|
+
false
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
@@ -51,7 +69,8 @@ module Authie
|
|
51
69
|
|
52
70
|
# Return the currently logged in user session
|
53
71
|
def auth_session
|
54
|
-
@auth_session ||= Session.get_session(@controller)
|
72
|
+
@auth_session ||= Authie::Session.get_session(@controller)
|
73
|
+
@auth_session == :none ? nil : @auth_session
|
55
74
|
end
|
56
75
|
|
57
76
|
private
|
@@ -60,6 +79,5 @@ module Authie
|
|
60
79
|
def cookies
|
61
80
|
@controller.send(:cookies)
|
62
81
|
end
|
63
|
-
|
64
82
|
end
|
65
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
|
@@ -31,6 +32,14 @@ module Authie
|
|
31
32
|
auth_session_delegate.current_user = user
|
32
33
|
end
|
33
34
|
|
35
|
+
def create_auth_session(user)
|
36
|
+
auth_session_delegate.create_auth_session(user)
|
37
|
+
end
|
38
|
+
|
39
|
+
def invalidate_auth_session
|
40
|
+
auth_session_delegate.invalidate_auth_session
|
41
|
+
end
|
42
|
+
|
34
43
|
def logged_in?
|
35
44
|
auth_session_delegate.logged_in?
|
36
45
|
end
|
@@ -38,6 +47,5 @@ module Authie
|
|
38
47
|
def auth_session
|
39
48
|
auth_session_delegate.auth_session
|
40
49
|
end
|
41
|
-
|
42
50
|
end
|
43
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
|
@@ -12,8 +13,6 @@ module Authie
|
|
12
13
|
require 'authie/controller_extension'
|
13
14
|
include Authie::ControllerExtension
|
14
15
|
end
|
15
|
-
|
16
16
|
end
|
17
|
-
|
18
17
|
end
|
19
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,46 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'secure_random_string'
|
4
|
+
|
1
5
|
module Authie
|
2
6
|
class Session < ActiveRecord::Base
|
3
|
-
|
4
7
|
# Errors which will be raised when there's an issue with a session's
|
5
8
|
# validity in the request.
|
6
9
|
class ValidityError < Error; end
|
10
|
+
|
7
11
|
class InactiveSession < ValidityError; end
|
12
|
+
|
8
13
|
class ExpiredSession < ValidityError; end
|
14
|
+
|
9
15
|
class BrowserMismatch < ValidityError; end
|
16
|
+
|
10
17
|
class HostMismatch < ValidityError; end
|
11
18
|
|
12
19
|
class NoParentSessionForRevert < Error; end
|
13
20
|
|
14
21
|
# Set table name
|
15
|
-
self.table_name =
|
22
|
+
self.table_name = 'authie_sessions'
|
16
23
|
|
17
24
|
# Relationships
|
18
|
-
|
19
|
-
user_options[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
|
20
|
-
belongs_to :user, user_options
|
21
|
-
|
22
|
-
parent_options = {:class_name => "Authie::Session"}
|
25
|
+
parent_options = { class_name: 'Authie::Session' }
|
23
26
|
parent_options[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
|
24
|
-
belongs_to :parent, parent_options
|
27
|
+
belongs_to :parent, **parent_options
|
25
28
|
|
26
29
|
# Scopes
|
27
|
-
scope :active, -> { where(:
|
28
|
-
scope :asc, -> { order(:
|
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) }
|
29
33
|
|
30
34
|
# Attributes
|
31
35
|
serialize :data, Hash
|
32
|
-
attr_accessor :controller
|
33
|
-
attr_accessor :temporary_token
|
36
|
+
attr_accessor :controller, :temporary_token
|
34
37
|
|
35
38
|
before_validation do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
self.user_agent = user_agent[0, 255] if user_agent.is_a?(String)
|
40
|
+
|
41
|
+
self.last_activity_path = last_activity_path[0, 255] if last_activity_path.is_a?(String)
|
39
42
|
end
|
40
43
|
|
41
44
|
before_create do
|
42
|
-
self.temporary_token =
|
43
|
-
self.token_hash = self.class.hash_token(
|
45
|
+
self.temporary_token = SecureRandomString.new(44)
|
46
|
+
self.token_hash = self.class.hash_token(temporary_token)
|
44
47
|
if controller
|
45
48
|
self.user_agent = controller.request.user_agent
|
46
49
|
set_cookie!
|
@@ -51,84 +54,119 @@ module Authie
|
|
51
54
|
cookies.delete(:user_session) if controller
|
52
55
|
end
|
53
56
|
|
57
|
+
# Return the user that
|
58
|
+
def user
|
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
|
+
end
|
64
|
+
|
65
|
+
# Set the user
|
66
|
+
def user=(user)
|
67
|
+
if user
|
68
|
+
self.user_type = user.class.name
|
69
|
+
self.user_id = user.id
|
70
|
+
else
|
71
|
+
self.user_type = nil
|
72
|
+
self.user_id = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
54
76
|
# This method should be called each time a user performs an
|
55
77
|
# action while authenticated with this session.
|
56
78
|
def touch!
|
57
|
-
|
79
|
+
check_security!
|
58
80
|
self.last_activity_at = Time.now
|
59
81
|
self.last_activity_ip = controller.request.ip
|
60
82
|
self.last_activity_path = controller.request.path
|
61
83
|
self.requests += 1
|
62
|
-
|
84
|
+
save!
|
63
85
|
Authie.config.events.dispatch(:session_touched, self)
|
64
86
|
true
|
65
87
|
end
|
66
88
|
|
67
89
|
# Sets the cookie on the associated controller.
|
68
|
-
|
90
|
+
# rubocop:disable Naming/AccessorMethodName
|
91
|
+
def set_cookie!(value = temporary_token)
|
69
92
|
cookies[:user_session] = {
|
70
|
-
:value
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
93
|
+
value: value,
|
94
|
+
secure: controller.request.ssl?,
|
95
|
+
httponly: true,
|
96
|
+
expires: expires_at
|
74
97
|
}
|
75
98
|
Authie.config.events.dispatch(:session_cookie_updated, self)
|
76
99
|
true
|
77
100
|
end
|
101
|
+
# rubocop:enable Naming/AccessorMethodName
|
102
|
+
|
103
|
+
# Sets the cookie for the parent session on the associated controller.
|
104
|
+
def set_parent_cookie!
|
105
|
+
cookies[:parent_user_session] = {
|
106
|
+
value: cookies[:user_session],
|
107
|
+
secure: controller.request.ssl?,
|
108
|
+
httponly: true,
|
109
|
+
expires: expires_at
|
110
|
+
}
|
111
|
+
Authie.config.events.dispatch(:parent_session_cookie_updated, self)
|
112
|
+
true
|
113
|
+
end
|
78
114
|
|
79
115
|
# Check the security of the session to ensure it can be used.
|
80
116
|
def check_security!
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
if self.inactive?
|
101
|
-
invalidate!
|
102
|
-
Authie.config.events.dispatch(:inactive_session_error, self)
|
103
|
-
raise InactiveSession, "Non-persistent session has expired"
|
104
|
-
end
|
105
|
-
|
106
|
-
if self.host && self.host != controller.request.host
|
107
|
-
invalidate!
|
108
|
-
Authie.config.events.dispatch(:host_mismatch_error, self)
|
109
|
-
raise HostMismatch, "Session was created on #{self.host} but accessed using #{controller.request.host}"
|
110
|
-
end
|
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'
|
111
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}"
|
147
|
+
end
|
148
|
+
|
149
|
+
true
|
112
150
|
end
|
113
151
|
|
114
152
|
# Has this persistent session expired?
|
115
153
|
def expired?
|
116
|
-
|
117
|
-
|
154
|
+
expires_at &&
|
155
|
+
expires_at < Time.now
|
118
156
|
end
|
119
157
|
|
120
158
|
# Has a non-persistent session become inactive?
|
121
159
|
def inactive?
|
122
|
-
|
123
|
-
|
124
|
-
|
160
|
+
expires_at.nil? &&
|
161
|
+
last_activity_at &&
|
162
|
+
last_activity_at < Authie.config.session_inactivity_timeout.ago
|
125
163
|
end
|
126
164
|
|
127
165
|
# Allow this session to persist rather than expiring at the end of the
|
128
166
|
# current browser session
|
129
167
|
def persist!
|
130
168
|
self.expires_at = Authie.config.persistent_session_length.from_now
|
131
|
-
|
169
|
+
save!
|
132
170
|
set_cookie!
|
133
171
|
end
|
134
172
|
|
@@ -140,16 +178,14 @@ module Authie
|
|
140
178
|
# Activate an old session
|
141
179
|
def activate!
|
142
180
|
self.active = true
|
143
|
-
|
181
|
+
save!
|
144
182
|
end
|
145
183
|
|
146
184
|
# Mark this session as invalid
|
147
185
|
def invalidate!
|
148
186
|
self.active = false
|
149
|
-
|
150
|
-
if controller
|
151
|
-
cookies.delete(:user_session)
|
152
|
-
end
|
187
|
+
save!
|
188
|
+
cookies.delete(:user_session) if controller
|
153
189
|
Authie.config.events.dispatch(:session_invalidated, self)
|
154
190
|
true
|
155
191
|
end
|
@@ -158,7 +194,7 @@ module Authie
|
|
158
194
|
def set(key, value)
|
159
195
|
self.data ||= {}
|
160
196
|
self.data[key.to_s] = value
|
161
|
-
|
197
|
+
save!
|
162
198
|
end
|
163
199
|
|
164
200
|
# Get some additional data from this session
|
@@ -168,71 +204,71 @@ module Authie
|
|
168
204
|
|
169
205
|
# Invalidate all sessions but this one for this user
|
170
206
|
def invalidate_others!
|
171
|
-
self.class.where(
|
172
|
-
s.invalidate!
|
173
|
-
end
|
207
|
+
self.class.where('id != ?', id).for_user(user).each(&:invalidate!)
|
174
208
|
end
|
175
209
|
|
176
210
|
# Note that we have just seen the user enter their password.
|
177
211
|
def see_password!
|
178
212
|
self.password_seen_at = Time.now
|
179
|
-
|
213
|
+
save!
|
180
214
|
Authie.config.events.dispatch(:seen_password, self)
|
181
215
|
true
|
182
216
|
end
|
183
217
|
|
184
218
|
# Have we seen the user's password recently in this sesion?
|
185
219
|
def recently_seen_password?
|
186
|
-
!!(
|
220
|
+
!!(password_seen_at && password_seen_at >= Authie.config.sudo_session_timeout.ago)
|
187
221
|
end
|
188
222
|
|
189
223
|
# Is two factor authentication required for this request?
|
190
224
|
def two_factored?
|
191
|
-
!!(two_factored_at ||
|
225
|
+
!!(two_factored_at || parent_id)
|
192
226
|
end
|
193
227
|
|
194
228
|
# Mark this request as two factor authoritsed
|
195
229
|
def mark_as_two_factored!
|
196
230
|
self.two_factored_at = Time.now
|
197
231
|
self.two_factored_ip = controller.request.ip
|
198
|
-
|
232
|
+
save!
|
199
233
|
Authie.config.events.dispatch(:marked_as_two_factored, self)
|
200
234
|
true
|
201
235
|
end
|
202
236
|
|
203
237
|
# Create a new session for impersonating for the given user
|
204
238
|
def impersonate!(user)
|
205
|
-
|
239
|
+
set_parent_cookie!
|
240
|
+
self.class.start(controller, user: user, parent: self)
|
206
241
|
end
|
207
242
|
|
208
243
|
# Revert back to the parent session
|
209
244
|
def revert_to_parent!
|
210
|
-
|
211
|
-
|
212
|
-
self.parent.activate!
|
213
|
-
self.parent.controller = self.controller
|
214
|
-
self.parent.set_cookie!
|
215
|
-
self.parent
|
216
|
-
else
|
217
|
-
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.'
|
218
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
|
219
255
|
end
|
220
256
|
|
221
257
|
# Is this the first session for this session's browser?
|
222
258
|
def first_session_for_browser?
|
223
|
-
self.class.where(
|
259
|
+
self.class.where('id < ?', id).for_user(user).where(browser_id: browser_id).empty?
|
224
260
|
end
|
225
261
|
|
226
262
|
# Is this the first session for the IP?
|
227
263
|
def first_session_for_ip?
|
228
|
-
self.class.where(
|
264
|
+
self.class.where('id < ?', id).for_user(user).where(login_ip: login_ip).empty?
|
229
265
|
end
|
230
266
|
|
231
267
|
# Find a session from the database for the given controller instance.
|
232
268
|
# Returns a session object or :none if no session is found.
|
233
269
|
def self.get_session(controller)
|
234
270
|
cookies = controller.send(:cookies)
|
235
|
-
if cookies[:user_session] && session =
|
271
|
+
if cookies[:user_session] && (session = find_session_by_token(cookies[:user_session]))
|
236
272
|
session.temporary_token = cookies[:user_session]
|
237
273
|
session.controller = controller
|
238
274
|
session
|
@@ -244,15 +280,19 @@ module Authie
|
|
244
280
|
# Find a session by a token (either from a hash or from the raw token)
|
245
281
|
def self.find_session_by_token(token)
|
246
282
|
return nil if token.blank?
|
247
|
-
|
283
|
+
|
284
|
+
active.where('token = ? OR token_hash = ?', token, hash_token(token)).first
|
248
285
|
end
|
249
286
|
|
250
287
|
# Create a new session and return the newly created session object.
|
251
288
|
# Any other sessions for the browser will be invalidated.
|
252
289
|
def self.start(controller, params = {})
|
253
290
|
cookies = controller.send(:cookies)
|
254
|
-
|
255
|
-
|
291
|
+
active.where(browser_id: cookies[:browser_id]).each(&:invalidate!)
|
292
|
+
user_object = params.delete(:user)
|
293
|
+
|
294
|
+
session = new(params)
|
295
|
+
session.user = user_object
|
256
296
|
session.controller = controller
|
257
297
|
session.browser_id = cookies[:browser_id]
|
258
298
|
session.login_at = Time.now
|
@@ -267,9 +307,10 @@ module Authie
|
|
267
307
|
def self.cleanup
|
268
308
|
Authie.config.events.dispatch(:before_cleanup)
|
269
309
|
# Invalidate transient sessions that haven't been used
|
270
|
-
|
310
|
+
active.where('expires_at IS NULL AND last_activity_at < ?',
|
311
|
+
Authie.config.session_inactivity_timeout.ago).each(&:invalidate!)
|
271
312
|
# Invalidate persistent sessions that have expired
|
272
|
-
|
313
|
+
active.where('expires_at IS NOT NULL AND expires_at < ?', Time.now).each(&:invalidate!)
|
273
314
|
Authie.config.events.dispatch(:after_cleanup)
|
274
315
|
true
|
275
316
|
end
|
@@ -281,9 +322,9 @@ module Authie
|
|
281
322
|
|
282
323
|
# Convert all existing active sessions to store their tokens in the database
|
283
324
|
def self.convert_tokens_to_hashes
|
284
|
-
active.where(:
|
285
|
-
hash =
|
286
|
-
|
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)
|
287
328
|
end
|
288
329
|
end
|
289
330
|
|
@@ -293,6 +334,5 @@ module Authie
|
|
293
334
|
def cookies
|
294
335
|
controller.send(:cookies)
|
295
336
|
end
|
296
|
-
|
297
337
|
end
|
298
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,42 +1,29 @@
|
|
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
|
-
cert_chain:
|
11
|
-
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
VR0RBBMwEYEPbWVAYWRhbWNvb2tlLmlvMBoGA1UdEgQTMBGBD21lQGFkYW1jb29r
|
28
|
-
ZS5pbzANBgkqhkiG9w0BAQsFAAOCAYEAkbz/AJwBsRKwgt2BhWqgr/egf/37IS3s
|
29
|
-
utVox7feYutKyFDHXYvCjm64XUJNioG7ipbRwOOGs5bEYfwgkabcAQnxSlkdNjc4
|
30
|
-
JIgL/cF4YRg8uJG7DH+LwpydXHqr7RneDiONuiHlEN/1EZZ8tjwXypdwzhQ2/6ot
|
31
|
-
YOxdSi/mXdoDoFlIebsLyInUZjqnm7dQ9nTTUNSB+1LoOD8ARNhTIPnKCnxwZd56
|
32
|
-
giOxoHuJIOhgi6U2zicZJHv8lUj2Lc3bcirQk5eeOFRPVGQSpLLoqA7dtS7Jy4cv
|
33
|
-
3c5m+HyxSxzlrcVHMAgJYemK0uhVQD9Y6JwHKDroWDH+MPALjlScw8ui1jmNuH31
|
34
|
-
n5JOH/07C4gYcwTjJmtoRSov46Z6Gn5cc6NFkQpA185pbRLqEDKzusXvBOQlAOLh
|
35
|
-
iyQrH6PJ0xgVJNYx+DLq3eFmo2hYJkw/lVhYAK+MdajtYJbD5VvCIEHO0d5RRgV+
|
36
|
-
qnCNZoPPy0UtRmGKZTMZvVJEZiw4g0fY
|
37
|
-
-----END CERTIFICATE-----
|
38
|
-
date: 2018-03-14 00:00:00.000000000 Z
|
39
|
-
dependencies: []
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: secure_random_string
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
40
27
|
description: A Rails library for storing user sessions in a backend database
|
41
28
|
email:
|
42
29
|
- me@adamcooke.io
|
@@ -81,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
68
|
- !ruby/object:Gem::Version
|
82
69
|
version: '0'
|
83
70
|
requirements: []
|
84
|
-
|
85
|
-
rubygems_version: 2.7.4
|
71
|
+
rubygems_version: 3.0.3
|
86
72
|
signing_key:
|
87
73
|
specification_version: 4
|
88
74
|
summary: A Rails library for storing user sessions in a backend database
|
checksums.yaml.gz.sig
DELETED
Binary file
|
data.tar.gz.sig
DELETED
Binary file
|
metadata.gz.sig
DELETED
Binary file
|