Empact-authlogic 2.1.5 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Empact-authlogic.gemspec +190 -187
- data/Gemfile +10 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +1 -1
- data/README.rdoc +14 -10
- data/Rakefile +5 -5
- data/VERSION.yml +3 -3
- data/lib/authlogic/acts_as_authentic/base.rb +14 -12
- data/lib/authlogic/acts_as_authentic/email.rb +12 -12
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +8 -9
- data/lib/authlogic/acts_as_authentic/login.rb +19 -18
- data/lib/authlogic/acts_as_authentic/password.rb +3 -3
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +15 -15
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +1 -1
- data/lib/authlogic/authenticates_many/base.rb +3 -4
- data/lib/authlogic/controller_adapters/rails_adapter.rb +1 -1
- data/lib/authlogic/random.rb +1 -1
- data/lib/authlogic/session/active_record_trickery.rb +8 -0
- data/lib/authlogic/session/callbacks.rb +2 -2
- data/lib/authlogic/session/cookies.rb +54 -2
- data/lib/authlogic/session/foundation.rb +17 -3
- data/lib/authlogic/session/http_auth.rb +43 -2
- data/lib/authlogic/session/scopes.rb +9 -9
- data/lib/authlogic/test_case/mock_controller.rb +12 -2
- data/lib/generators/authlogic/USAGE +8 -0
- data/lib/generators/authlogic/session_generator.rb +14 -0
- data/lib/generators/authlogic/templates/session.rb +2 -0
- data/test/acts_as_authentic_test/base_test.rb +1 -1
- data/test/acts_as_authentic_test/email_test.rb +29 -21
- data/test/acts_as_authentic_test/logged_in_status_test.rb +1 -1
- data/test/acts_as_authentic_test/login_test.rb +1 -1
- data/test/acts_as_authentic_test/magic_columns_test.rb +1 -1
- data/test/acts_as_authentic_test/password_test.rb +1 -1
- data/test/acts_as_authentic_test/perishable_token_test.rb +1 -1
- data/test/acts_as_authentic_test/persistence_token_test.rb +1 -1
- data/test/acts_as_authentic_test/restful_authentication_test.rb +1 -1
- data/test/acts_as_authentic_test/session_maintenance_test.rb +1 -1
- data/test/acts_as_authentic_test/single_access_test.rb +1 -1
- data/test/authenticates_many_test.rb +1 -1
- data/test/crypto_provider_test/aes256_test.rb +1 -1
- data/test/crypto_provider_test/bcrypt_test.rb +1 -1
- data/test/crypto_provider_test/sha1_test.rb +1 -1
- data/test/crypto_provider_test/sha256_test.rb +1 -1
- data/test/crypto_provider_test/sha512_test.rb +1 -1
- data/test/i18n_test.rb +1 -1
- data/test/random_test.rb +1 -8
- data/test/session_test/activation_test.rb +1 -1
- data/test/session_test/active_record_trickery_test.rb +12 -2
- data/test/session_test/brute_force_protection_test.rb +1 -1
- data/test/session_test/callbacks_test.rb +1 -1
- data/test/session_test/cookies_test.rb +26 -2
- data/test/session_test/existence_test.rb +1 -1
- data/test/session_test/http_auth_test.rb +31 -3
- data/test/session_test/id_test.rb +1 -1
- data/test/session_test/klass_test.rb +1 -1
- data/test/session_test/magic_columns_test.rb +1 -1
- data/test/session_test/magic_states_test.rb +1 -1
- data/test/session_test/params_test.rb +1 -1
- data/test/session_test/password_test.rb +1 -1
- data/test/session_test/perishability_test.rb +1 -1
- data/test/session_test/persistence_test.rb +1 -1
- data/test/session_test/scopes_test.rb +12 -12
- data/test/session_test/session_test.rb +1 -1
- data/test/session_test/timeout_test.rb +1 -1
- data/test/session_test/unauthorized_record_test.rb +1 -1
- data/test/session_test/validation_test.rb +1 -1
- data/test/test_helper.rb +27 -41
- metadata +50 -55
- data/.gitignore +0 -9
- data/CHANGELOG.rdoc +0 -353
@@ -11,7 +11,7 @@ module Authlogic
|
|
11
11
|
add_acts_as_authentic_module(Methods)
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
# Change how the perishable token works.
|
16
16
|
module Config
|
17
17
|
# When using the find_using_perishable_token method the token can expire. If the token is expired, no
|
@@ -23,7 +23,7 @@ module Authlogic
|
|
23
23
|
rw_config(:perishable_token_valid_for, (!value.nil? && value.to_i) || value, 10.minutes.to_i)
|
24
24
|
end
|
25
25
|
alias_method :perishable_token_valid_for=, :perishable_token_valid_for
|
26
|
-
|
26
|
+
|
27
27
|
# Authlogic tries to expire and change the perishable token as much as possible, without comprising
|
28
28
|
# it's purpose. This is for security reasons. If you want to manage it yourself, you can stop
|
29
29
|
# Authlogic from getting your in way by setting this to true.
|
@@ -35,21 +35,21 @@ module Authlogic
|
|
35
35
|
end
|
36
36
|
alias_method :disable_perishable_token_maintenance=, :disable_perishable_token_maintenance
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
# All methods relating to the perishable token.
|
40
40
|
module Methods
|
41
41
|
def self.included(klass)
|
42
42
|
return if !klass.column_names.include?("perishable_token")
|
43
|
-
|
43
|
+
|
44
44
|
klass.class_eval do
|
45
45
|
extend ClassMethods
|
46
46
|
include InstanceMethods
|
47
|
-
|
47
|
+
|
48
48
|
validates_uniqueness_of :perishable_token, :if => :perishable_token_changed?
|
49
49
|
before_save :reset_perishable_token, :unless => :disable_perishable_token_maintenance?
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# Class level methods for the perishable token
|
54
54
|
module ClassMethods
|
55
55
|
# Use this methdo to find a record with a perishable token. This method does 2 things for you:
|
@@ -63,37 +63,37 @@ module Authlogic
|
|
63
63
|
def find_using_perishable_token(token, age = self.perishable_token_valid_for)
|
64
64
|
return if token.blank?
|
65
65
|
age = age.to_i
|
66
|
-
|
66
|
+
|
67
67
|
conditions_sql = "perishable_token = ?"
|
68
68
|
conditions_subs = [token]
|
69
|
-
|
69
|
+
|
70
70
|
if column_names.include?("updated_at") && age > 0
|
71
71
|
conditions_sql += " and updated_at > ?"
|
72
72
|
conditions_subs << age.seconds.ago
|
73
73
|
end
|
74
|
-
|
75
|
-
|
74
|
+
|
75
|
+
where(conditions_sql, *conditions_subs).first
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
# This method will raise ActiveRecord::NotFound if no record is found.
|
79
79
|
def find_using_perishable_token!(token, age = perishable_token_valid_for)
|
80
80
|
find_using_perishable_token(token, age) || raise(ActiveRecord::RecordNotFound)
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
# Instance level methods for the perishable token.
|
85
85
|
module InstanceMethods
|
86
86
|
# Resets the perishable token to a random friendly token.
|
87
87
|
def reset_perishable_token
|
88
88
|
self.perishable_token = Random.friendly_token
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
# Same as reset_perishable_token, but then saves the record afterwards.
|
92
92
|
def reset_perishable_token!
|
93
93
|
reset_perishable_token
|
94
|
-
save_without_session_maintenance(false)
|
94
|
+
save_without_session_maintenance(:validate => false)
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
# A convenience method based on the disable_perishable_token_maintenance configuration option.
|
98
98
|
def disable_perishable_token_maintenance?
|
99
99
|
self.class.disable_perishable_token_maintenance == true
|
@@ -53,7 +53,7 @@ module Authlogic
|
|
53
53
|
# Same as reset_persistence_token, but then saves the record.
|
54
54
|
def reset_persistence_token!
|
55
55
|
reset_persistence_token
|
56
|
-
save_without_session_maintenance(false)
|
56
|
+
save_without_session_maintenance(:validate => false)
|
57
57
|
end
|
58
58
|
alias_method :forget!, :reset_persistence_token!
|
59
59
|
|
@@ -23,7 +23,7 @@ module Authlogic
|
|
23
23
|
#
|
24
24
|
# * <tt>session_class:</tt> default: "#{name}Session",
|
25
25
|
# This is the related session class.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# * <tt>relationship_name:</tt> default: options[:session_class].klass_name.underscore.pluralize,
|
28
28
|
# This is the name of the relationship you want to use to scope everything. For example an Account has many Users. There should be a relationship
|
29
29
|
# called :users that you defined with a has_many. The reason we use the relationship is so you don't have to repeat yourself. The relatonship
|
@@ -42,14 +42,13 @@ module Authlogic
|
|
42
42
|
options[:relationship_name] ||= options[:session_class].klass_name.underscore.pluralize
|
43
43
|
class_eval <<-"end_eval", __FILE__, __LINE__
|
44
44
|
def #{name}
|
45
|
-
find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.
|
46
|
-
find_options.delete_if { |key, value| ![:conditions, :include, :joins].include?(key.to_sym) || value.nil? }
|
45
|
+
find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.scoped
|
47
46
|
@#{name} ||= Authlogic::AuthenticatesMany::Association.new(#{options[:session_class]}, find_options, #{options[:scope_cookies] ? "self.class.model_name.underscore + '_' + self.send(self.class.primary_key).to_s" : "nil"})
|
48
47
|
end
|
49
48
|
end_eval
|
50
49
|
end
|
51
50
|
end
|
52
|
-
|
51
|
+
|
53
52
|
::ActiveRecord::Base.extend(Base) if defined?(::ActiveRecord)
|
54
53
|
end
|
55
54
|
end
|
@@ -15,7 +15,7 @@ module Authlogic
|
|
15
15
|
|
16
16
|
def cookie_domain
|
17
17
|
@cookie_domain_key ||= Rails::VERSION::STRING >= '2.3' ? :domain : :session_domain
|
18
|
-
|
18
|
+
controller.request.session_options[@cookie_domain_key]
|
19
19
|
end
|
20
20
|
|
21
21
|
def request_content_type
|
data/lib/authlogic/random.rb
CHANGED
@@ -66,7 +66,7 @@ module Authlogic
|
|
66
66
|
base.define_callbacks *METHODS
|
67
67
|
|
68
68
|
# If Rails 3, support the new callback syntax
|
69
|
-
if base.singleton_class.method_defined?(:set_callback)
|
69
|
+
if base.send(base.respond_to?(:singleton_class) ? :singleton_class : :metaclass).method_defined?(:set_callback)
|
70
70
|
METHODS.each do |method|
|
71
71
|
base.class_eval <<-"end_eval", __FILE__, __LINE__
|
72
72
|
def self.#{method}(*methods, &block)
|
@@ -92,7 +92,7 @@ module Authlogic
|
|
92
92
|
|
93
93
|
def save_record(alternate_record = nil)
|
94
94
|
r = alternate_record || record
|
95
|
-
r.save_without_session_maintenance(false) if r && r.changed? && !r.readonly?
|
95
|
+
r.save_without_session_maintenance(:validate => false) if r && r.changed? && !r.readonly?
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
@@ -47,6 +47,24 @@ module Authlogic
|
|
47
47
|
rw_config(:remember_me_for, value, 3.months, :_read)
|
48
48
|
end
|
49
49
|
alias_method :remember_me_for=, :remember_me_for
|
50
|
+
|
51
|
+
# Should the cookie be set as secure? If true, the cookie will only be sent over SSL connections
|
52
|
+
#
|
53
|
+
# * <tt>Default:</tt> false
|
54
|
+
# * <tt>Accepts:</tt> Boolean
|
55
|
+
def secure(value = nil)
|
56
|
+
rw_config(:secure, value, false)
|
57
|
+
end
|
58
|
+
alias_method :secure=, :secure
|
59
|
+
|
60
|
+
# Should the cookie be set as httponly? If true, the cookie will not be accessable from javascript
|
61
|
+
#
|
62
|
+
# * <tt>Default:</tt> false
|
63
|
+
# * <tt>Accepts:</tt> Boolean
|
64
|
+
def httponly(value = nil)
|
65
|
+
rw_config(:httponly, value, false)
|
66
|
+
end
|
67
|
+
alias_method :httponly=, :httponly
|
50
68
|
end
|
51
69
|
|
52
70
|
# The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
|
@@ -91,7 +109,39 @@ module Authlogic
|
|
91
109
|
return unless remember_me?
|
92
110
|
remember_me_for.from_now
|
93
111
|
end
|
94
|
-
|
112
|
+
|
113
|
+
# If the cookie should be marked as secure (SSL only)
|
114
|
+
def secure
|
115
|
+
return @secure if defined?(@secure)
|
116
|
+
@secure = self.class.secure
|
117
|
+
end
|
118
|
+
|
119
|
+
# Accepts a boolean as to whether the cookie should be marked as secure. If true the cookie will only ever be sent over an SSL connection.
|
120
|
+
def secure=(value)
|
121
|
+
@secure = value
|
122
|
+
end
|
123
|
+
|
124
|
+
# See secure
|
125
|
+
def secure?
|
126
|
+
secure == true || secure == "true" || secure == "1"
|
127
|
+
end
|
128
|
+
|
129
|
+
# If the cookie should be marked as httponly (not accessable via javascript)
|
130
|
+
def httponly
|
131
|
+
return @httponly if defined?(@httponly)
|
132
|
+
@httponly = self.class.httponly
|
133
|
+
end
|
134
|
+
|
135
|
+
# Accepts a boolean as to whether the cookie should be marked as httponly. If true, the cookie will not be accessable from javascript
|
136
|
+
def httponly=(value)
|
137
|
+
@httponly = value
|
138
|
+
end
|
139
|
+
|
140
|
+
# See httponly
|
141
|
+
def httponly?
|
142
|
+
httponly == true || httponly == "true" || httponly == "1"
|
143
|
+
end
|
144
|
+
|
95
145
|
private
|
96
146
|
def cookie_key
|
97
147
|
build_key(self.class.cookie_key)
|
@@ -117,6 +167,8 @@ module Authlogic
|
|
117
167
|
controller.cookies[cookie_key] = {
|
118
168
|
:value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}",
|
119
169
|
:expires => remember_me_until,
|
170
|
+
:secure => secure,
|
171
|
+
:httponly => httponly,
|
120
172
|
:domain => controller.cookie_domain
|
121
173
|
}
|
122
174
|
end
|
@@ -127,4 +179,4 @@ module Authlogic
|
|
127
179
|
end
|
128
180
|
end
|
129
181
|
end
|
130
|
-
end
|
182
|
+
end
|
@@ -6,6 +6,9 @@ module Authlogic
|
|
6
6
|
module Foundation
|
7
7
|
def self.included(klass)
|
8
8
|
klass.class_eval do
|
9
|
+
class_attribute :acts_as_authentic_config
|
10
|
+
self.acts_as_authentic_config ||= {}
|
11
|
+
|
9
12
|
extend ClassMethods
|
10
13
|
include InstanceMethods
|
11
14
|
end
|
@@ -15,10 +18,13 @@ module Authlogic
|
|
15
18
|
private
|
16
19
|
def rw_config(key, value, default_value = nil, read_value = nil)
|
17
20
|
if value == read_value
|
18
|
-
return
|
19
|
-
|
21
|
+
return acts_as_authentic_config[key] if acts_as_authentic_config.include?(key)
|
22
|
+
rw_config(key, default_value)
|
20
23
|
else
|
21
|
-
|
24
|
+
config = acts_as_authentic_config.clone
|
25
|
+
config[key] = value
|
26
|
+
self.acts_as_authentic_config = config
|
27
|
+
value
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
@@ -53,6 +59,14 @@ module Authlogic
|
|
53
59
|
"#<#{self.class.name}: #{credentials.blank? ? "no credentials provided" : credentials.inspect}>"
|
54
60
|
end
|
55
61
|
|
62
|
+
def persisted?
|
63
|
+
!(new_record? || destroyed?)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_key
|
67
|
+
new_record? ? nil : [ self.send(self.class.primary_key) ]
|
68
|
+
end
|
69
|
+
|
56
70
|
private
|
57
71
|
def build_key(last_part)
|
58
72
|
last_part
|
@@ -28,6 +28,41 @@ module Authlogic
|
|
28
28
|
rw_config(:allow_http_basic_auth, value, true)
|
29
29
|
end
|
30
30
|
alias_method :allow_http_basic_auth=, :allow_http_basic_auth
|
31
|
+
|
32
|
+
# Whether or not to request HTTP authentication
|
33
|
+
#
|
34
|
+
# If set to true and no HTTP authentication credentials are sent with
|
35
|
+
# the request, the Rails controller method
|
36
|
+
# authenticate_or_request_with_http_basic will be used and a '401
|
37
|
+
# Authorization Required' header will be sent with the response. In
|
38
|
+
# most cases, this will cause the classic HTTP authentication popup to
|
39
|
+
# appear in the users browser.
|
40
|
+
#
|
41
|
+
# If set to false, the Rails controller method
|
42
|
+
# authenticate_with_http_basic is used and no 401 header is sent.
|
43
|
+
#
|
44
|
+
# Note: This parameter has no effect unless allow_http_basic_auth is
|
45
|
+
# true
|
46
|
+
#
|
47
|
+
# * <tt>Default:</tt> false
|
48
|
+
# * <tt>Accepts:</tt> Boolean
|
49
|
+
def request_http_basic_auth(value = nil)
|
50
|
+
rw_config(:request_http_basic_auth, value, false)
|
51
|
+
end
|
52
|
+
alias_method :request_http_basic_auth=, :request_http_basic_auth
|
53
|
+
|
54
|
+
# HTTP authentication realm
|
55
|
+
#
|
56
|
+
# Sets the HTTP authentication realm.
|
57
|
+
#
|
58
|
+
# Note: This option has no effect unless request_http_basic_auth is true
|
59
|
+
#
|
60
|
+
# * <tt>Default:</tt> 'Application'
|
61
|
+
# * <tt>Accepts:</tt> String
|
62
|
+
def http_basic_auth_realm(value = nil)
|
63
|
+
rw_config(:http_basic_auth_realm, value, 'Application')
|
64
|
+
end
|
65
|
+
alias_method :http_basic_auth_realm=, :http_basic_auth_realm
|
31
66
|
end
|
32
67
|
|
33
68
|
# Instance methods for the HTTP basic auth feature of authlogic.
|
@@ -38,13 +73,19 @@ module Authlogic
|
|
38
73
|
end
|
39
74
|
|
40
75
|
def persist_by_http_auth
|
41
|
-
|
76
|
+
login_proc = Proc.new do |login, password|
|
42
77
|
if !login.blank? && !password.blank?
|
43
78
|
send("#{login_field}=", login)
|
44
79
|
send("#{password_field}=", password)
|
45
80
|
return valid?
|
46
81
|
end
|
47
82
|
end
|
83
|
+
|
84
|
+
if self.class.request_http_basic_auth
|
85
|
+
controller.authenticate_or_request_with_http_basic(self.class.http_basic_auth_realm, &login_proc)
|
86
|
+
else
|
87
|
+
controller.authenticate_with_http_basic(&login_proc)
|
88
|
+
end
|
48
89
|
|
49
90
|
false
|
50
91
|
end
|
@@ -55,4 +96,4 @@ module Authlogic
|
|
55
96
|
end
|
56
97
|
end
|
57
98
|
end
|
58
|
-
end
|
99
|
+
end
|
@@ -12,14 +12,14 @@ module Authlogic
|
|
12
12
|
attr_writer :scope
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
# = Scopes
|
17
17
|
module ClassMethods
|
18
18
|
# The current scope set, should be used in the block passed to with_scope.
|
19
19
|
def scope
|
20
20
|
Thread.current[:authlogic_scope]
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# What with_scopes focuses on is scoping the query when finding the object and the name of the cookie / session. It works very similar to
|
24
24
|
# ActiveRecord::Base#with_scopes. It accepts a hash with any of the following options:
|
25
25
|
#
|
@@ -34,11 +34,11 @@ module Authlogic
|
|
34
34
|
#
|
35
35
|
# Eseentially what the above does is scope the searching of the object with the sql you provided. So instead of:
|
36
36
|
#
|
37
|
-
# User.
|
37
|
+
# User.where("login = 'ben'").first
|
38
38
|
#
|
39
39
|
# it would be:
|
40
40
|
#
|
41
|
-
# User.
|
41
|
+
# User.where("login = 'ben' and account_id = 2").first
|
42
42
|
#
|
43
43
|
# You will also notice the :id option. This works just like the id method. It scopes your cookies. So the name of your cookie will be:
|
44
44
|
#
|
@@ -65,31 +65,31 @@ module Authlogic
|
|
65
65
|
self.scope = nil
|
66
66
|
result
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
private
|
70
70
|
def scope=(value)
|
71
71
|
Thread.current[:authlogic_scope] = value
|
72
72
|
end
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
module InstanceMethods
|
76
76
|
# Setting the scope if it exists upon instantiation.
|
77
77
|
def initialize(*args)
|
78
78
|
self.scope = self.class.scope
|
79
79
|
super
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# The scope of the current object
|
83
83
|
def scope
|
84
84
|
@scope ||= {}
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
private
|
88
88
|
# Used for things like cookie_key, session_key, etc.
|
89
89
|
def build_key(last_part)
|
90
90
|
[scope[:id], super].compact.join("_")
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
def search_for_record(*args)
|
94
94
|
klass.send(:with_scope, :find => (scope[:find_options] || {})) do
|
95
95
|
klass.send(*args)
|
@@ -3,7 +3,7 @@ module Authlogic
|
|
3
3
|
# Basically acts like a controller but doesn't do anything. Authlogic can interact with this, do it's thing and then you
|
4
4
|
# can look at the controller object to see if anything changed.
|
5
5
|
class MockController < ControllerAdapters::AbstractAdapter
|
6
|
-
attr_accessor :http_user, :http_password
|
6
|
+
attr_accessor :http_user, :http_password, :realm
|
7
7
|
attr_writer :request_content_type
|
8
8
|
|
9
9
|
def initialize
|
@@ -13,6 +13,12 @@ module Authlogic
|
|
13
13
|
yield http_user, http_password
|
14
14
|
end
|
15
15
|
|
16
|
+
def authenticate_or_request_with_http_basic(realm = 'DefaultRealm', &block)
|
17
|
+
self.realm = realm
|
18
|
+
@http_auth_requested = true
|
19
|
+
yield http_user, http_password
|
20
|
+
end
|
21
|
+
|
16
22
|
def cookies
|
17
23
|
@cookies ||= MockCookieJar.new
|
18
24
|
end
|
@@ -40,6 +46,10 @@ module Authlogic
|
|
40
46
|
def session
|
41
47
|
@session ||= {}
|
42
48
|
end
|
49
|
+
|
50
|
+
def http_auth_requested?
|
51
|
+
@http_auth_requested ||= false
|
52
|
+
end
|
43
53
|
end
|
44
54
|
end
|
45
|
-
end
|
55
|
+
end
|