authie 3.4.0 → 4.0.0.rc2
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/lib/authie/config.rb +18 -31
- data/lib/authie/controller_delegate.rb +56 -26
- data/lib/authie/controller_extension.rb +13 -36
- data/lib/authie/event_manager.rb +2 -0
- data/lib/authie/session.rb +231 -279
- data/lib/authie/session_model.rb +141 -0
- data/lib/authie/user.rb +1 -1
- data/lib/authie/version.rb +6 -1
- metadata +214 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5347676808e3554dde1670a91c8402ed166be59767298dfea961a9020659843
|
4
|
+
data.tar.gz: 03ee0e9cc60e7ae24a9e83c7a6ead5d72ebec109ef558898395d476014843a65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 219676dee96408c5cb85432ab4d919967415b9e575cb9681c4b4a9d210f041213af2a841a63ebdd4c88d9a8d3856f4ab306ddfd5e4947dee6e3d3654de8538b0
|
7
|
+
data.tar.gz: 7dd321dd2d407fd73b89ea6e1894d60362ac7e4fd792d0d74ea9e2cc5178b30eebd8d42ab3bf7baa356bc3d6f161d6809e0c545d9430d6db3267e8dfbda87190
|
data/lib/authie/config.rb
CHANGED
@@ -4,42 +4,29 @@ require 'authie/event_manager'
|
|
4
4
|
|
5
5
|
module Authie
|
6
6
|
class Config
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@session_inactivity_timeout || 12.hours
|
13
|
-
end
|
14
|
-
attr_writer :session_inactivity_timeout, :persistent_session_length, :sudo_session_timeout, :browser_id_cookie_name
|
15
|
-
|
16
|
-
def persistent_session_length
|
17
|
-
@persistent_session_length || 2.months
|
18
|
-
end
|
19
|
-
|
20
|
-
def sudo_session_timeout
|
21
|
-
@sudo_session_timeout || 10.minutes
|
22
|
-
end
|
7
|
+
attr_accessor :session_inactivity_timeout
|
8
|
+
attr_accessor :persistent_session_length
|
9
|
+
attr_accessor :sudo_session_timeout
|
10
|
+
attr_accessor :browser_id_cookie_name
|
11
|
+
attr_accessor :events
|
23
12
|
|
24
|
-
def
|
25
|
-
@
|
13
|
+
def initialize
|
14
|
+
@session_inactivity_timeout = 12.hours
|
15
|
+
@persistent_session_length = 2.months
|
16
|
+
@sudo_session_timeout = 10.minutes
|
17
|
+
@browser_id_cookie_name = :browser_id
|
18
|
+
@events = EventManager.new
|
26
19
|
end
|
20
|
+
end
|
27
21
|
|
28
|
-
|
29
|
-
|
22
|
+
class << self
|
23
|
+
def config
|
24
|
+
@config ||= Config.new
|
30
25
|
end
|
31
26
|
|
32
|
-
def
|
33
|
-
|
27
|
+
def configure(&block)
|
28
|
+
block.call(config)
|
29
|
+
config
|
34
30
|
end
|
35
31
|
end
|
36
|
-
|
37
|
-
def self.config
|
38
|
-
@config ||= Config.new
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.configure(&block)
|
42
|
-
block.call(config)
|
43
|
-
config
|
44
|
-
end
|
45
32
|
end
|
@@ -2,18 +2,28 @@
|
|
2
2
|
|
3
3
|
require 'securerandom'
|
4
4
|
require 'authie/session'
|
5
|
+
require 'authie/config'
|
6
|
+
require 'authie/session_model'
|
5
7
|
|
6
8
|
module Authie
|
9
|
+
# The controller delegate implements methods that can be used by a controller. These are then
|
10
|
+
# extended into controllers as needed (see ControllerExtension).
|
7
11
|
class ControllerDelegate
|
12
|
+
# @param controller [ActionController::Base]
|
13
|
+
# @return [Authie::ControllerDelegate]
|
8
14
|
def initialize(controller)
|
9
15
|
@controller = controller
|
10
16
|
end
|
11
17
|
|
12
|
-
#
|
18
|
+
# Sets a browser ID. This must be performed on any page request where AUthie will be used.
|
19
|
+
# It should be triggered before any other Authie provided methods. This will ensure that
|
20
|
+
# the given browser ID is unique.
|
21
|
+
#
|
22
|
+
# @return [String] the generated browser ID
|
13
23
|
def set_browser_id
|
14
24
|
until cookies[Authie.config.browser_id_cookie_name]
|
15
25
|
proposed_browser_id = SecureRandom.uuid
|
16
|
-
next if Authie::
|
26
|
+
next if Authie::SessionModel.where(browser_id: proposed_browser_id).exists?
|
17
27
|
|
18
28
|
cookies[Authie.config.browser_id_cookie_name] = {
|
19
29
|
value: proposed_browser_id,
|
@@ -21,61 +31,81 @@ module Authie
|
|
21
31
|
httponly: true,
|
22
32
|
secure: @controller.request.ssl?
|
23
33
|
}
|
24
|
-
# Dispatch an event when the browser ID is set.
|
25
34
|
Authie.config.events.dispatch(:set_browser_id, proposed_browser_id)
|
26
35
|
end
|
36
|
+
proposed_browser_id
|
27
37
|
end
|
28
38
|
|
29
|
-
# Touch the
|
39
|
+
# Touch the session on each request to ensure that it is validated and all last activity
|
40
|
+
# information is updated. This will return the session if one has been touched otherwise
|
41
|
+
# it will reteurn false if there is no session/not logged in. It is safe to run this on
|
42
|
+
# all requests even if there is no session.
|
43
|
+
#
|
44
|
+
# @return [Authie::Session, false]
|
30
45
|
def touch_auth_session
|
31
|
-
auth_session.touch
|
46
|
+
return auth_session.touch if logged_in?
|
47
|
+
|
48
|
+
false
|
32
49
|
end
|
33
50
|
|
34
|
-
# Return the currently logged in user
|
51
|
+
# Return the user for the currently logged in user or nil if no user is logged in
|
52
|
+
#
|
53
|
+
# @return [ActiveRecord::Base, nil]
|
35
54
|
def current_user
|
36
|
-
|
37
|
-
end
|
55
|
+
return nil unless logged_in?
|
38
56
|
|
39
|
-
|
40
|
-
def current_user=(user)
|
41
|
-
create_auth_session(user)
|
57
|
+
auth_session.session.user
|
42
58
|
end
|
43
59
|
|
44
|
-
# Create a new session for the given user
|
60
|
+
# Create a new session for the given user. If nil is provided as a user, the existing session
|
61
|
+
# will be invalidated.
|
62
|
+
#
|
63
|
+
# @return [Authie::Session, nil]
|
45
64
|
def create_auth_session(user)
|
46
65
|
if user
|
47
66
|
@auth_session = Authie::Session.start(@controller, user: user)
|
48
|
-
|
49
|
-
auth_session.invalidate! if logged_in?
|
50
|
-
@auth_session = :none
|
67
|
+
return @auth_session
|
51
68
|
end
|
69
|
+
|
70
|
+
invalidate_auth_session
|
71
|
+
nil
|
52
72
|
end
|
53
73
|
|
54
|
-
# Invalidate
|
74
|
+
# Invalidate the existing auth session if one exists. Return true if a sesion has been invalidated
|
75
|
+
# otherwise return false.
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
55
78
|
def invalidate_auth_session
|
56
79
|
if logged_in?
|
57
|
-
auth_session.invalidate
|
58
|
-
@auth_session =
|
59
|
-
true
|
60
|
-
else
|
61
|
-
false
|
80
|
+
auth_session.invalidate
|
81
|
+
@auth_session = nil
|
82
|
+
return true
|
62
83
|
end
|
84
|
+
|
85
|
+
false
|
63
86
|
end
|
64
87
|
|
65
|
-
# Is anyone currently logged in?
|
88
|
+
# Is anyone currently logged in? Return true if there is an auth session present.
|
89
|
+
#
|
90
|
+
# Note: this does not check the validatity of the session. You must always ensure that the `validate`
|
91
|
+
# or `touch` method is invoked to ensure that the session that has been found is active.
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
66
94
|
def logged_in?
|
67
95
|
auth_session.is_a?(Session)
|
68
96
|
end
|
69
97
|
|
70
|
-
# Return
|
98
|
+
# Return an auth session that has been found in the current cookies.
|
99
|
+
#
|
100
|
+
# @return [Authie::Session]
|
71
101
|
def auth_session
|
72
|
-
@auth_session
|
73
|
-
|
102
|
+
return @auth_session if instance_variable_defined?('@auth_session')
|
103
|
+
|
104
|
+
@auth_session = Authie::Session.get_session(@controller)
|
74
105
|
end
|
75
106
|
|
76
107
|
private
|
77
108
|
|
78
|
-
# Return cookies for the controller
|
79
109
|
def cookies
|
80
110
|
@controller.send(:cookies)
|
81
111
|
end
|
@@ -4,10 +4,19 @@ require 'authie/controller_delegate'
|
|
4
4
|
|
5
5
|
module Authie
|
6
6
|
module ControllerExtension
|
7
|
-
|
8
|
-
base
|
9
|
-
|
10
|
-
|
7
|
+
class << self
|
8
|
+
def included(base)
|
9
|
+
base.helper_method :logged_in?, :current_user, :auth_session
|
10
|
+
base.before_action :set_browser_id, :touch_auth_session
|
11
|
+
|
12
|
+
base.delegate :set_browser_id, to: :auth_session_delegate
|
13
|
+
base.delegate :touch_auth_session, to: :auth_session_delegate
|
14
|
+
base.delegate :current_user, to: :auth_session_delegate
|
15
|
+
base.delegate :create_auth_session, to: :auth_session_delegate
|
16
|
+
base.delegate :invalidate_auth_session, to: :auth_session_delegate
|
17
|
+
base.delegate :logged_in?, to: :auth_session_delegate
|
18
|
+
base.delegate :auth_session, to: :auth_session_delegate
|
19
|
+
end
|
11
20
|
end
|
12
21
|
|
13
22
|
private
|
@@ -15,37 +24,5 @@ module Authie
|
|
15
24
|
def auth_session_delegate
|
16
25
|
@auth_session_delegate ||= Authie::ControllerDelegate.new(self)
|
17
26
|
end
|
18
|
-
|
19
|
-
def set_browser_id
|
20
|
-
auth_session_delegate.set_browser_id
|
21
|
-
end
|
22
|
-
|
23
|
-
def touch_auth_session
|
24
|
-
auth_session_delegate.touch_auth_session
|
25
|
-
end
|
26
|
-
|
27
|
-
def current_user
|
28
|
-
auth_session_delegate.current_user
|
29
|
-
end
|
30
|
-
|
31
|
-
def current_user=(user)
|
32
|
-
auth_session_delegate.current_user = user
|
33
|
-
end
|
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
|
-
|
43
|
-
def logged_in?
|
44
|
-
auth_session_delegate.logged_in?
|
45
|
-
end
|
46
|
-
|
47
|
-
def auth_session
|
48
|
-
auth_session_delegate.auth_session
|
49
|
-
end
|
50
27
|
end
|
51
28
|
end
|
data/lib/authie/event_manager.rb
CHANGED
data/lib/authie/session.rb
CHANGED
@@ -1,338 +1,290 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'authie/session_model'
|
4
|
+
require 'authie/error'
|
5
|
+
require 'authie/config'
|
6
|
+
require 'active_support/core_ext/module/delegation'
|
4
7
|
|
5
8
|
module Authie
|
6
|
-
class Session
|
7
|
-
#
|
8
|
-
#
|
9
|
+
class Session
|
10
|
+
# The underlying session model instance
|
11
|
+
#
|
12
|
+
# @return [Authie::SessionModel]
|
13
|
+
attr_reader :session
|
14
|
+
|
15
|
+
# A parent class that encapsulates all session validity errors.
|
9
16
|
class ValidityError < Error; end
|
10
17
|
|
18
|
+
# Raised when a session is used but it is no longer active
|
11
19
|
class InactiveSession < ValidityError; end
|
12
20
|
|
21
|
+
# Raised when a session is used but it has expired
|
13
22
|
class ExpiredSession < ValidityError; end
|
14
23
|
|
24
|
+
# Raised when a session is used but the browser ID does not match
|
15
25
|
class BrowserMismatch < ValidityError; end
|
16
26
|
|
27
|
+
# Raised when a session is used but the hostname does not match
|
28
|
+
# the session hostname
|
17
29
|
class HostMismatch < ValidityError; end
|
18
30
|
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
@
|
31
|
+
# Initialize a new session object
|
32
|
+
#
|
33
|
+
# @param controller [ActionController::Base] any controller
|
34
|
+
# @param session [Authie::SessionModel] an Authie session model instance
|
35
|
+
# @return [Authie::Session]
|
36
|
+
def initialize(controller, session)
|
37
|
+
@controller = controller
|
38
|
+
@session = session
|
39
|
+
end
|
40
|
+
|
41
|
+
# Validate that the session is valid and raise and error if not
|
42
|
+
#
|
43
|
+
# @raises [Authie::Session::BrowserMismatch]
|
44
|
+
# @raises [Authie::Session::InactiveSession]
|
45
|
+
# @raises [Authie::Session::ExpiredSession]
|
46
|
+
# @raises [Authie::Session::HostMismatch]
|
47
|
+
# @return [Authie::Session]
|
48
|
+
def validate
|
49
|
+
validate_browser_id
|
50
|
+
validate_active
|
51
|
+
validate_expiry
|
52
|
+
validate_inactivity
|
53
|
+
validate_host
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Mark the current session as persistent. Will set the expiry time of the underlying
|
58
|
+
# session and update the cookie.
|
59
|
+
#
|
60
|
+
# @raises [ActiveRecord::RecordInvalid]
|
61
|
+
# @return [Authie::Session]
|
62
|
+
def persist
|
63
|
+
@session.expires_at = Authie.config.persistent_session_length.from_now
|
64
|
+
@session.save!
|
65
|
+
set_cookie
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
# Invalidates the current session by marking it inactive and removing the current cookie.
|
70
|
+
#
|
71
|
+
# @raises [ActiveRecord::RecordInvalid]
|
72
|
+
# @return [Authie::Session]
|
73
|
+
def invalidate
|
74
|
+
@session.invalidate!
|
75
|
+
cookies.delete(:user_session)
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Touches the current session to ensure it is currently valid and to update attributes
|
80
|
+
# which should be updatd on each request. This will raise the same errors as the #validate
|
81
|
+
# method. It will set the last activity time, IP and path as well as incrementing
|
82
|
+
# the request counter.
|
83
|
+
#
|
84
|
+
# @raises [Authie::Session::BrowserMismatch]
|
85
|
+
# @raises [Authie::Session::InactiveSession]
|
86
|
+
# @raises [Authie::Session::ExpiredSession]
|
87
|
+
# @raises [Authie::Session::HostMismatch]
|
88
|
+
# @raises [ActiveRecord::RecordInvalid]
|
89
|
+
# @return [Authie::Session]
|
90
|
+
def touch
|
91
|
+
validate
|
92
|
+
@session.last_activity_at = Time.now
|
93
|
+
@session.last_activity_ip = @controller.request.ip
|
94
|
+
@session.last_activity_path = @controller.request.path
|
95
|
+
@session.requests += 1
|
96
|
+
@session.save!
|
97
|
+
Authie.config.events.dispatch(:session_touched, self)
|
98
|
+
self
|
63
99
|
end
|
64
100
|
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
101
|
+
# Mark the session's password as seen at the current time
|
102
|
+
#
|
103
|
+
# @raises [ActiveRecord::RecordInvalid]
|
104
|
+
# @return [Authie::Session]
|
105
|
+
def see_password
|
106
|
+
@session.password_seen_at = Time.now
|
107
|
+
@session.save!
|
108
|
+
Authie.config.events.dispatch(:seen_password, self)
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Mark this request as two factored by setting the time and the current
|
113
|
+
# IP address.
|
114
|
+
#
|
115
|
+
# @raises [ActiveRecord::RecordInvalid]
|
116
|
+
# @return [Authie::Session]
|
117
|
+
def mark_as_two_factored
|
118
|
+
@session.two_factored_at = Time.now
|
119
|
+
@session.two_factored_ip = @controller.request.ip
|
120
|
+
@session.save!
|
121
|
+
Authie.config.events.dispatch(:marked_as_two_factor, self)
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
# Starts a new session by setting the cookie. This should be invoked whenever
|
126
|
+
# a new session begins. It usually does not need to be called directly as it
|
127
|
+
# will be taken care of by the class-level start method.
|
128
|
+
#
|
129
|
+
# @return [Authie::Session]
|
130
|
+
def start
|
131
|
+
set_cookie
|
132
|
+
Authie.config.events.dispatch(:start_session, session)
|
133
|
+
self
|
74
134
|
end
|
75
135
|
|
76
|
-
|
77
|
-
# action while authenticated with this session.
|
78
|
-
def touch!
|
79
|
-
check_security!
|
80
|
-
self.last_activity_at = Time.now
|
81
|
-
self.last_activity_ip = controller.request.ip
|
82
|
-
self.last_activity_path = controller.request.path
|
83
|
-
self.requests += 1
|
84
|
-
save!
|
85
|
-
Authie.config.events.dispatch(:session_touched, self)
|
86
|
-
true
|
87
|
-
end
|
136
|
+
private
|
88
137
|
|
89
|
-
# Sets the cookie on the associated controller.
|
90
138
|
# rubocop:disable Naming/AccessorMethodName
|
91
|
-
def set_cookie
|
139
|
+
def set_cookie(value = @session.temporary_token)
|
92
140
|
cookies[:user_session] = {
|
93
141
|
value: value,
|
94
|
-
secure: controller.request.ssl?,
|
142
|
+
secure: @controller.request.ssl?,
|
95
143
|
httponly: true,
|
96
|
-
expires: expires_at
|
144
|
+
expires: @session.expires_at
|
97
145
|
}
|
98
146
|
Authie.config.events.dispatch(:session_cookie_updated, self)
|
99
147
|
true
|
100
148
|
end
|
101
149
|
# rubocop:enable Naming/AccessorMethodName
|
102
150
|
|
103
|
-
|
104
|
-
|
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
|
151
|
+
def cookies
|
152
|
+
@controller.send(:cookies)
|
113
153
|
end
|
114
154
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
if cookies[:browser_id] != browser_id
|
120
|
-
invalidate!
|
155
|
+
def validate_browser_id
|
156
|
+
if cookies[:browser_id] != @session.browser_id
|
157
|
+
invalidate
|
121
158
|
Authie.config.events.dispatch(:browser_id_mismatch_error, self)
|
122
159
|
raise BrowserMismatch, 'Browser ID mismatch'
|
123
160
|
end
|
124
161
|
|
125
|
-
|
126
|
-
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
def validate_active
|
166
|
+
unless @session.active?
|
167
|
+
invalidate
|
127
168
|
Authie.config.events.dispatch(:invalid_session_error, self)
|
128
169
|
raise InactiveSession, 'Session is no longer active'
|
129
170
|
end
|
130
171
|
|
131
|
-
|
132
|
-
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
def validate_expiry
|
176
|
+
if @session.expired?
|
177
|
+
invalidate
|
133
178
|
Authie.config.events.dispatch(:expired_session_error, self)
|
134
179
|
raise ExpiredSession, 'Persistent session has expired'
|
135
180
|
end
|
136
181
|
|
137
|
-
|
138
|
-
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
def validate_inactivity
|
186
|
+
if @session.inactive?
|
187
|
+
invalidate
|
139
188
|
Authie.config.events.dispatch(:inactive_session_error, self)
|
140
189
|
raise InactiveSession, 'Non-persistent session has expired'
|
141
190
|
end
|
142
191
|
|
143
|
-
|
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
|
150
|
-
end
|
151
|
-
|
152
|
-
# Has this persistent session expired?
|
153
|
-
def expired?
|
154
|
-
expires_at &&
|
155
|
-
expires_at < Time.now
|
156
|
-
end
|
157
|
-
|
158
|
-
# Has a non-persistent session become inactive?
|
159
|
-
def inactive?
|
160
|
-
expires_at.nil? &&
|
161
|
-
last_activity_at &&
|
162
|
-
last_activity_at < Authie.config.session_inactivity_timeout.ago
|
163
|
-
end
|
164
|
-
|
165
|
-
# Allow this session to persist rather than expiring at the end of the
|
166
|
-
# current browser session
|
167
|
-
def persist!
|
168
|
-
self.expires_at = Authie.config.persistent_session_length.from_now
|
169
|
-
save!
|
170
|
-
set_cookie!
|
171
|
-
end
|
172
|
-
|
173
|
-
# Is this a persistent session?
|
174
|
-
def persistent?
|
175
|
-
!!expires_at
|
176
|
-
end
|
177
|
-
|
178
|
-
# Activate an old session
|
179
|
-
def activate!
|
180
|
-
self.active = true
|
181
|
-
save!
|
182
|
-
end
|
183
|
-
|
184
|
-
# Mark this session as invalid
|
185
|
-
def invalidate!
|
186
|
-
self.active = false
|
187
|
-
save!
|
188
|
-
cookies.delete(:user_session) if controller
|
189
|
-
Authie.config.events.dispatch(:session_invalidated, self)
|
190
|
-
true
|
191
|
-
end
|
192
|
-
|
193
|
-
# Set some additional data in this session
|
194
|
-
def set(key, value)
|
195
|
-
self.data ||= {}
|
196
|
-
self.data[key.to_s] = value
|
197
|
-
save!
|
198
|
-
end
|
199
|
-
|
200
|
-
# Get some additional data from this session
|
201
|
-
def get(key)
|
202
|
-
(self.data ||= {})[key.to_s]
|
203
|
-
end
|
204
|
-
|
205
|
-
# Invalidate all sessions but this one for this user
|
206
|
-
def invalidate_others!
|
207
|
-
self.class.where('id != ?', id).for_user(user).each(&:invalidate!)
|
208
|
-
end
|
209
|
-
|
210
|
-
# Note that we have just seen the user enter their password.
|
211
|
-
def see_password!
|
212
|
-
self.password_seen_at = Time.now
|
213
|
-
save!
|
214
|
-
Authie.config.events.dispatch(:seen_password, self)
|
215
|
-
true
|
216
|
-
end
|
217
|
-
|
218
|
-
# Have we seen the user's password recently in this sesion?
|
219
|
-
def recently_seen_password?
|
220
|
-
!!(password_seen_at && password_seen_at >= Authie.config.sudo_session_timeout.ago)
|
221
|
-
end
|
222
|
-
|
223
|
-
# Is two factor authentication required for this request?
|
224
|
-
def two_factored?
|
225
|
-
!!(two_factored_at || parent_id)
|
192
|
+
self
|
226
193
|
end
|
227
194
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
Authie.config.events.dispatch(:marked_as_two_factored, self)
|
234
|
-
true
|
235
|
-
end
|
236
|
-
|
237
|
-
# Create a new session for impersonating for the given user
|
238
|
-
def impersonate!(user)
|
239
|
-
set_parent_cookie!
|
240
|
-
self.class.start(controller, user: user, parent: self)
|
241
|
-
end
|
242
|
-
|
243
|
-
# Revert back to the parent session
|
244
|
-
def revert_to_parent!
|
245
|
-
unless parent && cookies[:parent_user_session]
|
246
|
-
raise NoParentSessionForRevert, 'Session does not have a parent therefore cannot be reverted.'
|
195
|
+
def validate_host
|
196
|
+
if @session.host && @session.host != @controller.request.host
|
197
|
+
invalidate
|
198
|
+
Authie.config.events.dispatch(:host_mismatch_error, self)
|
199
|
+
raise HostMismatch, "Session was created on #{@session.host} but accessed using #{@controller.request.host}"
|
247
200
|
end
|
248
201
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
session.
|
273
|
-
session.controller = controller
|
274
|
-
session
|
275
|
-
else
|
276
|
-
:none
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
class << self
|
206
|
+
# Create a new session within the given controller for the
|
207
|
+
#
|
208
|
+
# @param controller [ActionController::Base]
|
209
|
+
# @option params [ActiveRecord::Base] user
|
210
|
+
# @return [Authie::Session]
|
211
|
+
def start(controller, params = {})
|
212
|
+
cookies = controller.send(:cookies)
|
213
|
+
SessionModel.active.where(browser_id: cookies[:browser_id]).each(&:invalidate!)
|
214
|
+
user_object = params.delete(:user)
|
215
|
+
|
216
|
+
session = SessionModel.new(params)
|
217
|
+
session.user = user_object
|
218
|
+
session.browser_id = cookies[:browser_id]
|
219
|
+
session.login_at = Time.now
|
220
|
+
session.login_ip = controller.request.ip
|
221
|
+
session.host = controller.request.host
|
222
|
+
session.user_agent = controller.request.user_agent
|
223
|
+
session.save!
|
224
|
+
|
225
|
+
new(controller, session).start
|
277
226
|
end
|
278
|
-
end
|
279
|
-
|
280
|
-
# Find a session by a token (either from a hash or from the raw token)
|
281
|
-
def self.find_session_by_token(token)
|
282
|
-
return nil if token.blank?
|
283
227
|
|
284
|
-
|
285
|
-
|
228
|
+
# Lookup a session for a given controller and return the session
|
229
|
+
# object.
|
230
|
+
#
|
231
|
+
# @param controller [ActionController::Base]
|
232
|
+
# @return [Authie::Session]
|
233
|
+
def get_session(controller)
|
234
|
+
cookies = controller.send(:cookies)
|
235
|
+
return nil if cookies[:user_session].blank?
|
286
236
|
|
287
|
-
|
288
|
-
|
289
|
-
def self.start(controller, params = {})
|
290
|
-
cookies = controller.send(:cookies)
|
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
|
296
|
-
session.controller = controller
|
297
|
-
session.browser_id = cookies[:browser_id]
|
298
|
-
session.login_at = Time.now
|
299
|
-
session.login_ip = controller.request.ip
|
300
|
-
session.host = controller.request.host
|
301
|
-
session.save!
|
302
|
-
Authie.config.events.dispatch(:start_session, session)
|
303
|
-
session
|
304
|
-
end
|
237
|
+
session = SessionModel.find_session_by_token(cookies[:user_session])
|
238
|
+
return nil if session.blank?
|
305
239
|
|
306
|
-
|
307
|
-
|
308
|
-
Authie.config.events.dispatch(:before_cleanup)
|
309
|
-
# Invalidate transient sessions that haven't been used
|
310
|
-
active.where('expires_at IS NULL AND last_activity_at < ?',
|
311
|
-
Authie.config.session_inactivity_timeout.ago).each(&:invalidate!)
|
312
|
-
# Invalidate persistent sessions that have expired
|
313
|
-
active.where('expires_at IS NOT NULL AND expires_at < ?', Time.now).each(&:invalidate!)
|
314
|
-
Authie.config.events.dispatch(:after_cleanup)
|
315
|
-
true
|
316
|
-
end
|
317
|
-
|
318
|
-
# Return a hash of a given token
|
319
|
-
def self.hash_token(token)
|
320
|
-
Digest::SHA256.hexdigest(token)
|
321
|
-
end
|
322
|
-
|
323
|
-
# Convert all existing active sessions to store their tokens in the database
|
324
|
-
def self.convert_tokens_to_hashes
|
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)
|
240
|
+
session.temporary_token = cookies[:user_session]
|
241
|
+
new(controller, session)
|
328
242
|
end
|
329
|
-
end
|
330
|
-
|
331
|
-
private
|
332
243
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
244
|
+
delegate :hash_token, to: SessionModel
|
245
|
+
end
|
246
|
+
|
247
|
+
# Backwards compatibility with Authie < 4.0. These methods were all available on sessions
|
248
|
+
# in previous versions of Authie. They have been maintained for backwards-compatibility but
|
249
|
+
# will be removed entirely in Authie 5.0.
|
250
|
+
alias check_security! validate
|
251
|
+
alias persist! persist
|
252
|
+
alias invalidate! invalidate
|
253
|
+
alias touch! touch
|
254
|
+
alias set_cookie! set_cookie
|
255
|
+
alias see_password! see_password
|
256
|
+
alias mark_as_two_factored! mark_as_two_factored
|
257
|
+
|
258
|
+
# Delegate key methods back to the underlying session model. Previous behaviour in Authie
|
259
|
+
# exposed all methods on the session model. It is useful that these methods can be accessed
|
260
|
+
# easily from this session proxy model so these are maintained as delegated methods.
|
261
|
+
delegate :active?, to: :session
|
262
|
+
delegate :browser_id, to: :session
|
263
|
+
delegate :expired?, to: :session
|
264
|
+
delegate :first_session_for_browser?, to: :session
|
265
|
+
delegate :first_session_for_ip?, to: :session
|
266
|
+
delegate :get, to: :session
|
267
|
+
delegate :inactive?, to: :session
|
268
|
+
delegate :invalidate_others!, to: :session
|
269
|
+
delegate :last_activity_at, to: :session
|
270
|
+
delegate :last_activity_ip, to: :session
|
271
|
+
delegate :last_activity_path, to: :session
|
272
|
+
delegate :login_at, to: :session
|
273
|
+
delegate :login_ip, to: :session
|
274
|
+
delegate :password_seen_at, to: :session
|
275
|
+
delegate :persisted?, to: :session
|
276
|
+
delegate :persistent?, to: :session
|
277
|
+
delegate :recently_seen_password?, to: :session
|
278
|
+
delegate :requests, to: :session
|
279
|
+
delegate :set, to: :session
|
280
|
+
delegate :temporary_token, to: :session
|
281
|
+
delegate :token_hash, to: :session
|
282
|
+
delegate :two_factored_at, to: :session
|
283
|
+
delegate :two_factored_ip, to: :session
|
284
|
+
delegate :two_factored?, to: :session
|
285
|
+
delegate :update, to: :session
|
286
|
+
delegate :update!, to: :session
|
287
|
+
delegate :user_agent, to: :session
|
288
|
+
delegate :user, to: :session
|
337
289
|
end
|
338
290
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/base'
|
4
|
+
require 'secure_random_string'
|
5
|
+
require 'authie/config'
|
6
|
+
|
7
|
+
module Authie
|
8
|
+
class SessionModel < ActiveRecord::Base
|
9
|
+
attr_accessor :temporary_token
|
10
|
+
|
11
|
+
self.table_name = 'authie_sessions'
|
12
|
+
|
13
|
+
belongs_to :parent, class_name: 'Authie::SessionModel', optional: true
|
14
|
+
|
15
|
+
scope :active, -> { where(active: true) }
|
16
|
+
scope :asc, -> { order(last_activity_at: :desc) }
|
17
|
+
scope :for_user, ->(user) { where(user_type: user.class.name, user_id: user.id) }
|
18
|
+
|
19
|
+
# Attributes
|
20
|
+
serialize :data, Hash
|
21
|
+
|
22
|
+
before_validation do
|
23
|
+
self.user_agent = user_agent[0, 255] if user_agent.is_a?(String)
|
24
|
+
self.last_activity_path = last_activity_path[0, 255] if last_activity_path.is_a?(String)
|
25
|
+
end
|
26
|
+
|
27
|
+
before_create do
|
28
|
+
self.temporary_token = SecureRandomString.new(44)
|
29
|
+
self.token_hash = self.class.hash_token(temporary_token)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the user that
|
33
|
+
def user
|
34
|
+
return unless user_id && user_type
|
35
|
+
return @user if instance_variable_defined?('@user')
|
36
|
+
|
37
|
+
@user = user_type.constantize.find_by(id: user_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set the user
|
41
|
+
def user=(user)
|
42
|
+
@user = user
|
43
|
+
if user
|
44
|
+
self.user_type = user.class.name
|
45
|
+
self.user_id = user.id
|
46
|
+
else
|
47
|
+
self.user_type = nil
|
48
|
+
self.user_id = nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def expired?
|
53
|
+
expires_at.present? &&
|
54
|
+
expires_at < Time.now
|
55
|
+
end
|
56
|
+
|
57
|
+
def inactive?
|
58
|
+
expires_at.nil? &&
|
59
|
+
last_activity_at.present? &&
|
60
|
+
last_activity_at < Authie.config.session_inactivity_timeout.ago
|
61
|
+
end
|
62
|
+
|
63
|
+
def persistent?
|
64
|
+
!!expires_at
|
65
|
+
end
|
66
|
+
|
67
|
+
def activate!
|
68
|
+
self.active = true
|
69
|
+
save!
|
70
|
+
end
|
71
|
+
|
72
|
+
def invalidate!
|
73
|
+
self.active = false
|
74
|
+
save!
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def set(key, value)
|
79
|
+
self.data ||= {}
|
80
|
+
self.data[key.to_s] = value
|
81
|
+
save!
|
82
|
+
end
|
83
|
+
|
84
|
+
def get(key)
|
85
|
+
(self.data ||= {})[key.to_s]
|
86
|
+
end
|
87
|
+
|
88
|
+
def invalidate_others!
|
89
|
+
self.class.where('id != ?', id).for_user(user).each(&:invalidate!).inspect
|
90
|
+
end
|
91
|
+
|
92
|
+
# Have we seen the user's password recently in this sesion?
|
93
|
+
def recently_seen_password?
|
94
|
+
!!(password_seen_at && password_seen_at >= Authie.config.sudo_session_timeout.ago)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Is two factor authentication required for this request?
|
98
|
+
def two_factored?
|
99
|
+
!!(two_factored_at || parent_id)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Is this the first session for this session's browser?
|
103
|
+
def first_session_for_browser?
|
104
|
+
self.class.where('id < ?', id).for_user(user).where(browser_id: browser_id).empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Is this the first session for the IP?
|
108
|
+
def first_session_for_ip?
|
109
|
+
self.class.where('id < ?', id).for_user(user).where(login_ip: login_ip).empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
# Find a session from the database for the given controller instance.
|
114
|
+
# Returns a session object or :none if no session is found.
|
115
|
+
|
116
|
+
# Find a session by a token (either from a hash or from the raw token)
|
117
|
+
def find_session_by_token(token)
|
118
|
+
return nil if token.blank?
|
119
|
+
|
120
|
+
active.where(token_hash: hash_token(token)).first
|
121
|
+
end
|
122
|
+
|
123
|
+
# Cleanup any old sessions.
|
124
|
+
def cleanup
|
125
|
+
Authie.config.events.dispatch(:before_cleanup)
|
126
|
+
# Invalidate transient sessions that haven't been used
|
127
|
+
active.where('expires_at IS NULL AND last_activity_at < ?',
|
128
|
+
Authie.config.session_inactivity_timeout.ago).each(&:invalidate!)
|
129
|
+
# Invalidate persistent sessions that have expired
|
130
|
+
active.where('expires_at IS NOT NULL AND expires_at < ?', Time.now).each(&:invalidate!)
|
131
|
+
Authie.config.events.dispatch(:after_cleanup)
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return a hash of a given token
|
136
|
+
def hash_token(token)
|
137
|
+
Digest::SHA256.hexdigest(token)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/authie/user.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Authie
|
4
4
|
module User
|
5
5
|
def self.included(base)
|
6
|
-
base.has_many :user_sessions, class_name: 'Authie::
|
6
|
+
base.has_many :user_sessions, class_name: 'Authie::SessionModel', as: :user, dependent: :delete_all
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
data/lib/authie/version.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Authie
|
4
|
-
|
4
|
+
VERSION_FILE_ROOT = File.expand_path('../../VERSION', __dir__)
|
5
|
+
VERSION = if File.file?(VERSION_FILE_ROOT)
|
6
|
+
File.read(VERSION_FILE_ROOT).strip.sub(/\Av/, '')
|
7
|
+
else
|
8
|
+
'0.0.0.dev'
|
9
|
+
end
|
5
10
|
end
|
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.rc2
|
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: 2022-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '8.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '8.0'
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: secure_random_string
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +44,194 @@ dependencies:
|
|
24
44
|
- - ">="
|
25
45
|
- !ruby/object:Gem::Version
|
26
46
|
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: appraisal
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.4.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.4.1
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rails
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.0'
|
68
|
+
- - "<"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '8.0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '5.0'
|
78
|
+
- - "<"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '8.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rspec
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rspec-core
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rspec-expectations
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: rspec-mocks
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: rspec-rails
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
- !ruby/object:Gem::Dependency
|
152
|
+
name: rubocop
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - '='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.17.0
|
158
|
+
type: :development
|
159
|
+
prerelease: false
|
160
|
+
version_requirements: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - '='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: 1.17.0
|
165
|
+
- !ruby/object:Gem::Dependency
|
166
|
+
name: simplecov
|
167
|
+
requirement: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
type: :development
|
173
|
+
prerelease: false
|
174
|
+
version_requirements: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: '0'
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: simplecov-console
|
181
|
+
requirement: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
type: :development
|
187
|
+
prerelease: false
|
188
|
+
version_requirements: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
- !ruby/object:Gem::Dependency
|
194
|
+
name: solargraph
|
195
|
+
requirement: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ">="
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0'
|
200
|
+
type: :development
|
201
|
+
prerelease: false
|
202
|
+
version_requirements: !ruby/object:Gem::Requirement
|
203
|
+
requirements:
|
204
|
+
- - ">="
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: '0'
|
207
|
+
- !ruby/object:Gem::Dependency
|
208
|
+
name: sqlite3
|
209
|
+
requirement: !ruby/object:Gem::Requirement
|
210
|
+
requirements:
|
211
|
+
- - '='
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: 1.4.2
|
214
|
+
type: :development
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
requirements:
|
218
|
+
- - '='
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: 1.4.2
|
221
|
+
- !ruby/object:Gem::Dependency
|
222
|
+
name: timecop
|
223
|
+
requirement: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '0'
|
228
|
+
type: :development
|
229
|
+
prerelease: false
|
230
|
+
version_requirements: !ruby/object:Gem::Requirement
|
231
|
+
requirements:
|
232
|
+
- - ">="
|
233
|
+
- !ruby/object:Gem::Version
|
234
|
+
version: '0'
|
27
235
|
description: A Rails library for storing user sessions in a backend database
|
28
236
|
email:
|
29
237
|
- me@adamcooke.io
|
@@ -47,6 +255,7 @@ files:
|
|
47
255
|
- lib/authie/event_manager.rb
|
48
256
|
- lib/authie/rack_controller.rb
|
49
257
|
- lib/authie/session.rb
|
258
|
+
- lib/authie/session_model.rb
|
50
259
|
- lib/authie/user.rb
|
51
260
|
- lib/authie/version.rb
|
52
261
|
homepage: https://github.com/adamcooke/authie
|
@@ -64,11 +273,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
273
|
version: '0'
|
65
274
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
275
|
requirements:
|
67
|
-
- - "
|
276
|
+
- - ">"
|
68
277
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
278
|
+
version: 1.3.1
|
70
279
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
280
|
+
rubygems_version: 3.3.7
|
72
281
|
signing_key:
|
73
282
|
specification_version: 4
|
74
283
|
summary: A Rails library for storing user sessions in a backend database
|