authlogic 2.0.1 → 2.1.2
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.
- data/.gitignore +9 -0
- data/CHANGELOG.rdoc +110 -1
- data/{MIT-LICENSE → LICENSE} +1 -1
- data/README.rdoc +138 -161
- data/Rakefile +40 -19
- data/VERSION.yml +4 -0
- data/authlogic.gemspec +212 -0
- data/init.rb +1 -1
- data/lib/authlogic/acts_as_authentic/base.rb +44 -35
- data/lib/authlogic/acts_as_authentic/email.rb +47 -14
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +20 -14
- data/lib/authlogic/acts_as_authentic/login.rb +84 -8
- data/lib/authlogic/acts_as_authentic/password.rb +207 -88
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +9 -4
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +4 -2
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +5 -4
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +16 -4
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -3
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +1 -1
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +13 -1
- data/lib/authlogic/controller_adapters/rails_adapter.rb +14 -4
- data/lib/authlogic/controller_adapters/sinatra_adapter.rb +61 -0
- data/lib/authlogic/crypto_providers/aes256.rb +2 -2
- data/lib/authlogic/crypto_providers/bcrypt.rb +3 -1
- data/lib/authlogic/crypto_providers/sha1.rb +2 -2
- data/lib/authlogic/i18n/translator.rb +15 -0
- data/lib/authlogic/i18n.rb +43 -14
- data/lib/authlogic/regex.rb +25 -0
- data/lib/authlogic/session/activation.rb +2 -0
- data/lib/authlogic/session/active_record_trickery.rb +26 -5
- data/lib/authlogic/session/brute_force_protection.rb +30 -11
- data/lib/authlogic/session/callbacks.rb +18 -7
- data/lib/authlogic/session/cookies.rb +21 -17
- data/lib/authlogic/session/existence.rb +6 -2
- data/lib/authlogic/session/foundation.rb +1 -1
- data/lib/authlogic/session/http_auth.rb +46 -11
- data/lib/authlogic/session/magic_columns.rb +29 -9
- data/lib/authlogic/session/magic_states.rb +6 -5
- data/lib/authlogic/session/params.rb +16 -10
- data/lib/authlogic/session/password.rb +115 -31
- data/lib/authlogic/session/priority_record.rb +1 -1
- data/lib/authlogic/session/session.rb +5 -3
- data/lib/authlogic/session/timeout.rb +1 -1
- data/lib/authlogic/session/validation.rb +13 -7
- data/lib/authlogic/test_case/mock_controller.rb +45 -0
- data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -0
- data/lib/authlogic/test_case/mock_logger.rb +10 -0
- data/lib/authlogic/test_case/mock_request.rb +19 -0
- data/lib/authlogic/test_case/rails_request_adapter.rb +30 -0
- data/lib/authlogic/test_case.rb +114 -0
- data/lib/authlogic.rb +4 -3
- data/rails/init.rb +1 -0
- data/shoulda_macros/authlogic.rb +3 -2
- data/test/acts_as_authentic_test/base_test.rb +6 -0
- data/test/acts_as_authentic_test/email_test.rb +25 -7
- data/test/acts_as_authentic_test/login_test.rb +37 -7
- data/test/acts_as_authentic_test/magic_columns_test.rb +4 -4
- data/test/acts_as_authentic_test/password_test.rb +46 -44
- data/test/acts_as_authentic_test/perishable_token_test.rb +36 -2
- data/test/acts_as_authentic_test/restful_authentication_test.rb +40 -0
- data/test/acts_as_authentic_test/session_maintenance_test.rb +26 -18
- data/test/acts_as_authentic_test/single_access_test.rb +6 -1
- data/test/i18n_test.rb +33 -0
- data/test/libs/affiliate.rb +7 -0
- data/test/libs/ldaper.rb +3 -0
- data/test/random_test.rb +2 -2
- 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 +37 -12
- data/test/session_test/cookies_test.rb +3 -3
- data/test/session_test/existence_test.rb +3 -3
- data/test/session_test/http_auth_test.rb +20 -8
- data/test/session_test/magic_columns_test.rb +9 -6
- data/test/session_test/magic_states_test.rb +3 -3
- data/test/session_test/params_test.rb +4 -4
- data/test/session_test/password_test.rb +23 -1
- data/test/session_test/session_test.rb +9 -9
- data/test/session_test/timeout_test.rb +10 -1
- data/test/test_helper.rb +53 -23
- metadata +42 -27
- data/Manifest.txt +0 -111
- data/lib/authlogic/testing/test_unit_helpers.rb +0 -39
- data/lib/authlogic/version.rb +0 -56
- data/test/libs/mock_controller.rb +0 -35
- data/test/libs/mock_cookie_jar.rb +0 -10
- data/test/libs/mock_request.rb +0 -5
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Authlogic bridge for Sinatra
|
|
2
|
+
module Authlogic
|
|
3
|
+
module ControllerAdapters
|
|
4
|
+
module SinatraAdapter
|
|
5
|
+
class Cookies
|
|
6
|
+
attr_reader :request, :response
|
|
7
|
+
|
|
8
|
+
def initialize(request, response)
|
|
9
|
+
@request = request
|
|
10
|
+
@response = response
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def delete(key, options = {})
|
|
14
|
+
@request.cookies.delete(key)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def []=(key, options)
|
|
18
|
+
@response.set_cookie(key, options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(meth, *args, &block)
|
|
22
|
+
@request.cookies.send(meth, *args, &block)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Controller
|
|
27
|
+
attr_reader :request, :response, :cookies
|
|
28
|
+
|
|
29
|
+
def initialize(request, response)
|
|
30
|
+
@request = request
|
|
31
|
+
@cookies = Cookies.new(request, response)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def session
|
|
35
|
+
env['rack.session']
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def method_missing(meth, *args, &block)
|
|
39
|
+
@request.send meth, *args, &block
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Adapter < AbstractAdapter
|
|
44
|
+
def cookie_domain
|
|
45
|
+
env['SERVER_NAME']
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module Implementation
|
|
49
|
+
def self.included(klass)
|
|
50
|
+
klass.send :before do
|
|
51
|
+
controller = Controller.new(request, response)
|
|
52
|
+
Authlogic::Session::Base.controller = Adapter.new(controller)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Sinatra::Request.send(:include, Authlogic::ControllerAdapters::SinatraAdapter::Adapter::Implementation)
|
|
@@ -5,7 +5,7 @@ module Authlogic
|
|
|
5
5
|
# This encryption method is reversible if you have the supplied key. So in order to use this encryption method you must supply it with a key first.
|
|
6
6
|
# In an initializer, or before your application initializes, you should do the following:
|
|
7
7
|
#
|
|
8
|
-
# Authlogic::CryptoProviders::AES256.key = "my really long and unique key,
|
|
8
|
+
# Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrably a bunch of random characters"
|
|
9
9
|
#
|
|
10
10
|
# My final comment is that this is a strong encryption method, but its main weakness is that its reversible. If you do not need to reverse the hash
|
|
11
11
|
# then you should consider Sha512 or BCrypt instead.
|
|
@@ -34,7 +34,7 @@ module Authlogic
|
|
|
34
34
|
|
|
35
35
|
private
|
|
36
36
|
def aes
|
|
37
|
-
raise ArgumentError.new("You provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
|
|
37
|
+
raise ArgumentError.new("You must provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
|
|
38
38
|
@aes ||= OpenSSL::Cipher::Cipher.new("AES-256-ECB")
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -35,7 +35,9 @@ module Authlogic
|
|
|
35
35
|
#
|
|
36
36
|
# Tell acts_as_authentic to use it:
|
|
37
37
|
#
|
|
38
|
-
# acts_as_authentic
|
|
38
|
+
# acts_as_authentic do |c|
|
|
39
|
+
# c.crypto_provider = Authlogic::CryptoProviders::BCrypt
|
|
40
|
+
# end
|
|
39
41
|
#
|
|
40
42
|
# You are good to go!
|
|
41
43
|
class BCrypt
|
|
@@ -2,8 +2,8 @@ require "digest/sha1"
|
|
|
2
2
|
|
|
3
3
|
module Authlogic
|
|
4
4
|
module CryptoProviders
|
|
5
|
-
# This class was made for the users transitioning from restful_authentication. I highly discourage using this
|
|
6
|
-
# Please use any other provider offered by Authlogic.
|
|
5
|
+
# This class was made for the users transitioning from restful_authentication. I highly discourage using this
|
|
6
|
+
# crypto provider as it inferior to your other options. Please use any other provider offered by Authlogic.
|
|
7
7
|
class Sha1
|
|
8
8
|
class << self
|
|
9
9
|
def join_token
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Authlogic
|
|
2
|
+
module I18n
|
|
3
|
+
class Translator
|
|
4
|
+
# If the I18n gem is present, calls +I18n.translate+ passing all
|
|
5
|
+
# arguments, else returns +options[:default]+.
|
|
6
|
+
def translate(key, options = {})
|
|
7
|
+
if defined?(::I18n)
|
|
8
|
+
::I18n.translate key, options
|
|
9
|
+
else
|
|
10
|
+
options[:default]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/authlogic/i18n.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "authlogic/i18n/translator"
|
|
2
|
+
|
|
1
3
|
module Authlogic
|
|
2
4
|
# This class allows any message in Authlogic to use internationalization. In earlier versions of Authlogic each message was translated via configuration.
|
|
3
5
|
# This cluttered up the configuration and cluttered up Authlogic. So all translation has been extracted out into this class. Now all messages pass through
|
|
@@ -11,25 +13,24 @@ module Authlogic
|
|
|
11
13
|
#
|
|
12
14
|
# Authlogic::I18n.t('error_messages.password_invalid', :default => "is invalid")
|
|
13
15
|
#
|
|
14
|
-
# If you use a different I18n library
|
|
16
|
+
# If you use a different I18n library just replace the build-in I18n::Translator class with your own. For example:
|
|
15
17
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# def t(key, options = {})
|
|
18
|
+
# class MyAuthlogicI18nTranslator
|
|
19
|
+
# def translate(key, options = {})
|
|
19
20
|
# # you will have key which will be something like: "error_messages.password_invalid"
|
|
20
21
|
# # you will also have options[:default], which will be the default english version of the message
|
|
21
22
|
# # do whatever you want here with the arguments passed to you.
|
|
22
23
|
# end
|
|
23
24
|
# end
|
|
24
25
|
#
|
|
25
|
-
# Authlogic::I18n.
|
|
26
|
+
# Authlogic::I18n.translator = MyAuthlogicI18nTranslator.new
|
|
26
27
|
#
|
|
27
28
|
# That it's! Here is a complete list of the keys that are passed. Just define these however you wish:
|
|
28
29
|
#
|
|
29
30
|
# authlogic:
|
|
30
31
|
# error_messages:
|
|
31
32
|
# login_blank: can not be blank
|
|
32
|
-
# login_not_found:
|
|
33
|
+
# login_not_found: is not valid
|
|
33
34
|
# login_invalid: should use only letters, numbers, spaces, and .-_@ please.
|
|
34
35
|
# consecutive_failed_logins_limit_exceeded: Consecutive failed logins limit exceeded, account is disabled.
|
|
35
36
|
# email_invalid: should look like an email address.
|
|
@@ -39,16 +40,44 @@ module Authlogic
|
|
|
39
40
|
# not_confirmed: Your account is not confirmed
|
|
40
41
|
# not_approved: Your account is not approved
|
|
41
42
|
# no_authentication_details: You did not provide any details for authentication.
|
|
42
|
-
|
|
43
|
+
# models:
|
|
44
|
+
# user_session: UserSession (or whatever name you are using)
|
|
45
|
+
# attributes:
|
|
46
|
+
# user_session: (or whatever name you are using)
|
|
47
|
+
# login: login
|
|
48
|
+
# email: email
|
|
49
|
+
# password: password
|
|
50
|
+
# remember_me: remember me
|
|
51
|
+
module I18n
|
|
52
|
+
@@scope = :authlogic
|
|
53
|
+
@@translator = nil
|
|
54
|
+
|
|
43
55
|
class << self
|
|
56
|
+
# Returns the current scope. Defaults to :authlogic
|
|
57
|
+
def scope
|
|
58
|
+
@@scope
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Sets the current scope. Used to set a custom scope.
|
|
62
|
+
def scope=(scope)
|
|
63
|
+
@@scope = scope
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns the current translator. Defaults to +Translator+.
|
|
67
|
+
def translator
|
|
68
|
+
@@translator ||= Translator.new
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Sets the current translator. Used to set a custom translator.
|
|
72
|
+
def translator=(translator)
|
|
73
|
+
@@translator = translator
|
|
74
|
+
end
|
|
75
|
+
|
|
44
76
|
# All message translation is passed to this method. The first argument is the key for the message. The second is options, see the rails I18n library for a list of options used.
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
::I18n.t(key, options.merge(:scope => :authlogic))
|
|
48
|
-
else
|
|
49
|
-
options[:default]
|
|
50
|
-
end
|
|
77
|
+
def translate(key, options = {})
|
|
78
|
+
translator.translate key, { :scope => I18n.scope }.merge(options)
|
|
51
79
|
end
|
|
80
|
+
alias :t :translate
|
|
52
81
|
end
|
|
53
82
|
end
|
|
54
|
-
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Authlogic
|
|
2
|
+
# This is a module the contains regular expressions used throughout Authlogic. The point of extracting
|
|
3
|
+
# them out into their own module is to make them easily available to you for other uses. Ex:
|
|
4
|
+
#
|
|
5
|
+
# validates_format_of :my_email_field, :with => Authlogic::Regex.email
|
|
6
|
+
module Regex
|
|
7
|
+
# A general email regular expression. It allows top level domains (TLD) to be from 2 - 4 in length, any
|
|
8
|
+
# TLD longer than that must be manually specified. The decisions behind this regular expression were made
|
|
9
|
+
# by reading this website: http://www.regular-expressions.info/email.html, which is an excellent resource
|
|
10
|
+
# for regular expressions.
|
|
11
|
+
def self.email
|
|
12
|
+
return @email_regex if @email_regex
|
|
13
|
+
email_name_regex = '[A-Z0-9_\.%\+\-]+'
|
|
14
|
+
domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
|
|
15
|
+
domain_tld_regex = '(?:[A-Z]{2,4}|museum|travel)'
|
|
16
|
+
@email_regex = /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# A simple regular expression that only allows for letters, numbers, spaces, and .-_@. Just a standard login / username
|
|
20
|
+
# regular expression.
|
|
21
|
+
def self.login
|
|
22
|
+
/\A\w[\w\.+\-_@ ]+\z/
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -29,6 +29,8 @@ module Authlogic
|
|
|
29
29
|
# This accepts a controller object wrapped with the Authlogic controller adapter. The controller adapters close the gap
|
|
30
30
|
# between the different controllers in each framework. That being said, Authlogic is expecting your object's class to
|
|
31
31
|
# extend Authlogic::ControllerAdapters::AbstractAdapter. See Authlogic::ControllerAdapters for more info.
|
|
32
|
+
#
|
|
33
|
+
# Lastly, this is thread safe.
|
|
32
34
|
def controller=(value)
|
|
33
35
|
Thread.current[:authlogic_controller] = value
|
|
34
36
|
end
|
|
@@ -11,12 +11,21 @@ module Authlogic
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
module ClassMethods
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# How to name the attributes of Authlogic, works JUST LIKE ActiveRecord, but instead it uses the following
|
|
15
|
+
# namespace:
|
|
16
|
+
#
|
|
17
|
+
# authlogic.attributes.user_session.login
|
|
18
|
+
def human_attribute_name(attribute_key_name, options = {})
|
|
19
|
+
options[:count] ||= 1
|
|
20
|
+
options[:default] ||= attribute_key_name.to_s.humanize
|
|
21
|
+
I18n.t("attributes.#{name.underscore}.#{attribute_key_name}", options)
|
|
16
22
|
end
|
|
17
23
|
|
|
24
|
+
# How to name the class, works JUST LIKE ActiveRecord, except it uses the following namespace:
|
|
25
|
+
#
|
|
26
|
+
# authlogic.models.user_session
|
|
18
27
|
def human_name(*args)
|
|
19
|
-
|
|
28
|
+
I18n.t("models.#{name.underscore}", {:count => 1, :default => name.humanize})
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
# For rails < 2.3, mispelled
|
|
@@ -24,17 +33,29 @@ module Authlogic
|
|
|
24
33
|
[self]
|
|
25
34
|
end
|
|
26
35
|
|
|
27
|
-
# For
|
|
36
|
+
# For rails >= 2.3, mispelling fixed
|
|
28
37
|
def self_and_descendants_from_active_record
|
|
29
38
|
[self]
|
|
30
39
|
end
|
|
40
|
+
|
|
41
|
+
# For rails >= 3.0
|
|
42
|
+
def model_name
|
|
43
|
+
clazz = defined?(::ActiveModel) ? ::ActiveModel::Name : ::ActiveSupport::ModelName
|
|
44
|
+
clazz.new(self.to_s)
|
|
45
|
+
end
|
|
31
46
|
end
|
|
32
47
|
|
|
33
48
|
module InstanceMethods
|
|
49
|
+
# Don't use this yourself, this is to just trick some of the helpers since this is the method it calls.
|
|
34
50
|
def new_record?
|
|
35
51
|
new_session?
|
|
36
52
|
end
|
|
53
|
+
|
|
54
|
+
# For rails >= 3.0
|
|
55
|
+
def to_model
|
|
56
|
+
self
|
|
57
|
+
end
|
|
37
58
|
end
|
|
38
59
|
end
|
|
39
60
|
end
|
|
40
|
-
end
|
|
61
|
+
end
|
|
@@ -19,7 +19,8 @@ module Authlogic
|
|
|
19
19
|
klass.class_eval do
|
|
20
20
|
extend Config
|
|
21
21
|
include InstanceMethods
|
|
22
|
-
validate :
|
|
22
|
+
validate :reset_failed_login_count, :if => :reset_failed_login_count?
|
|
23
|
+
validate :validate_failed_logins, :if => :being_brute_force_protected?
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
@@ -36,7 +37,7 @@ module Authlogic
|
|
|
36
37
|
# * <tt>Default:</tt> 50
|
|
37
38
|
# * <tt>Accepts:</tt> Integer, set to 0 to disable
|
|
38
39
|
def consecutive_failed_logins_limit(value = nil)
|
|
39
|
-
|
|
40
|
+
rw_config(:consecutive_failed_logins_limit, value, 50)
|
|
40
41
|
end
|
|
41
42
|
alias_method :consecutive_failed_logins_limit=, :consecutive_failed_logins_limit
|
|
42
43
|
|
|
@@ -45,31 +46,49 @@ module Authlogic
|
|
|
45
46
|
# * <tt>Default:</tt> 2.hours
|
|
46
47
|
# * <tt>Accepts:</tt> Fixnum, set to 0 for permanent ban
|
|
47
48
|
def failed_login_ban_for(value = nil)
|
|
48
|
-
|
|
49
|
+
rw_config(:failed_login_ban_for, (!value.nil? && value) || value, 2.hours.to_i)
|
|
49
50
|
end
|
|
50
51
|
alias_method :failed_login_ban_for=, :failed_login_ban_for
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# The methods available for an Authlogic::Session::Base object that make up the brute force protection feature.
|
|
54
55
|
module InstanceMethods
|
|
56
|
+
# Returns true when the consecutive_failed_logins_limit has been exceeded and is being temporarily banned.
|
|
57
|
+
# Notice the word temporary, the user will not be permanently banned unless you choose to do so with configuration.
|
|
58
|
+
# By default they will be banned for 2 hours. During that 2 hour period this method will return true.
|
|
59
|
+
def being_brute_force_protected?
|
|
60
|
+
exceeded_failed_logins_limit? && (failed_login_ban_for <= 0 ||
|
|
61
|
+
(attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
|
|
62
|
+
end
|
|
63
|
+
|
|
55
64
|
private
|
|
56
|
-
def
|
|
65
|
+
def exceeded_failed_logins_limit?
|
|
57
66
|
!attempted_record.nil? && attempted_record.respond_to?(:failed_login_count) && consecutive_failed_logins_limit > 0 &&
|
|
58
|
-
attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
|
|
59
|
-
(failed_login_ban_for <= 0 || (attempted_record.respond_to?(:updated_at) && attempted_record.updated_at >= failed_login_ban_for.seconds.ago))
|
|
67
|
+
attempted_record.failed_login_count && attempted_record.failed_login_count >= consecutive_failed_logins_limit
|
|
60
68
|
end
|
|
61
69
|
|
|
62
|
-
def
|
|
63
|
-
|
|
70
|
+
def reset_failed_login_count?
|
|
71
|
+
exceeded_failed_logins_limit? && !being_brute_force_protected?
|
|
64
72
|
end
|
|
65
73
|
|
|
66
|
-
def
|
|
67
|
-
|
|
74
|
+
def reset_failed_login_count
|
|
75
|
+
attempted_record.failed_login_count = 0
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
def validate_failed_logins
|
|
71
79
|
errors.clear # Clear all other error messages, as they are irrelevant at this point and can only provide additional information that is not needed
|
|
72
|
-
errors.
|
|
80
|
+
errors.add(:base, I18n.t(
|
|
81
|
+
'error_messages.consecutive_failed_logins_limit_exceeded',
|
|
82
|
+
:default => "Consecutive failed logins limit exceeded, account has been" + (failed_login_ban_for == 0 ? "" : " temporarily") + " disabled."
|
|
83
|
+
))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def consecutive_failed_logins_limit
|
|
87
|
+
self.class.consecutive_failed_logins_limit
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def failed_login_ban_for
|
|
91
|
+
self.class.failed_login_ban_for
|
|
73
92
|
end
|
|
74
93
|
end
|
|
75
94
|
end
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
module Authlogic
|
|
2
2
|
module Session
|
|
3
|
-
#
|
|
3
|
+
# Between these callsbacks and the configuration, this is the contract between me and you to safely
|
|
4
|
+
# modify Authlogic's behavior. I will do everything I can to make sure these do not change.
|
|
4
5
|
#
|
|
5
|
-
#
|
|
6
|
+
# Check out the sub modules of Authlogic::Session. They are very concise, clear, and to the point. More
|
|
7
|
+
# importantly they use the same API that you would use to extend Authlogic. That being said, they are great
|
|
8
|
+
# examples of how to extend Authlogic and add / modify behavior to Authlogic. These modules could easily be pulled out
|
|
9
|
+
# into their own plugin and become an "add on" without any change.
|
|
10
|
+
#
|
|
11
|
+
# Now to the point of this module. Just like in ActiveRecord you have before_save, before_validation, etc.
|
|
12
|
+
# You have similar callbacks with Authlogic, see the METHODS constant below. The order of execution is as follows:
|
|
6
13
|
#
|
|
7
14
|
# before_persisting
|
|
8
15
|
# persist
|
|
@@ -27,11 +34,13 @@ module Authlogic
|
|
|
27
34
|
# [save record if record.changed?]
|
|
28
35
|
#
|
|
29
36
|
# before_destroy
|
|
37
|
+
# [save record if record.changed?]
|
|
30
38
|
# destroy
|
|
31
39
|
# after_destroy
|
|
32
40
|
#
|
|
33
|
-
# Notice the "save record if changed?" lines above. This helps with performance. If you need to make
|
|
34
|
-
#
|
|
41
|
+
# Notice the "save record if changed?" lines above. This helps with performance. If you need to make
|
|
42
|
+
# changes to the associated record, there is no need to save the record, Authlogic will do it for you.
|
|
43
|
+
# This allows multiple modules to modify the record and execute as few queries as possible.
|
|
35
44
|
#
|
|
36
45
|
# **WARNING**: unlike ActiveRecord, these callbacks must be set up on the class level:
|
|
37
46
|
#
|
|
@@ -41,11 +50,13 @@ module Authlogic
|
|
|
41
50
|
# # ..etc
|
|
42
51
|
# end
|
|
43
52
|
#
|
|
44
|
-
# You can NOT define a "before_validation" method, this is bad practice and does not allow Authlogic
|
|
53
|
+
# You can NOT define a "before_validation" method, this is bad practice and does not allow Authlogic
|
|
54
|
+
# to extend properly with multiple extensions. Please ONLY use the method above.
|
|
45
55
|
module Callbacks
|
|
46
56
|
METHODS = [
|
|
47
57
|
"before_persisting", "persist", "after_persisting",
|
|
48
|
-
"before_validation", "before_validation_on_create", "before_validation_on_update", "validate",
|
|
58
|
+
"before_validation", "before_validation_on_create", "before_validation_on_update", "validate",
|
|
59
|
+
"after_validation_on_update", "after_validation_on_create", "after_validation",
|
|
49
60
|
"before_save", "before_create", "before_update", "after_update", "after_create", "after_save",
|
|
50
61
|
"before_destroy", "after_destroy"
|
|
51
62
|
]
|
|
@@ -70,7 +81,7 @@ module Authlogic
|
|
|
70
81
|
|
|
71
82
|
def save_record(alternate_record = nil)
|
|
72
83
|
r = alternate_record || record
|
|
73
|
-
r.save_without_session_maintenance(false) if r && r.changed?
|
|
84
|
+
r.save_without_session_maintenance(false) if r && r.changed? && !r.readonly?
|
|
74
85
|
end
|
|
75
86
|
end
|
|
76
87
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Authlogic
|
|
2
2
|
module Session
|
|
3
|
-
# Handles all authentication that deals with cookies, such as persisting
|
|
3
|
+
# Handles all authentication that deals with cookies, such as persisting, saving, and destroying.
|
|
4
4
|
module Cookies
|
|
5
5
|
def self.included(klass)
|
|
6
6
|
klass.class_eval do
|
|
@@ -26,7 +26,7 @@ module Authlogic
|
|
|
26
26
|
# * <tt>Default:</tt> "#{klass_name.underscore}_credentials"
|
|
27
27
|
# * <tt>Accepts:</tt> String
|
|
28
28
|
def cookie_key(value = nil)
|
|
29
|
-
|
|
29
|
+
rw_config(:cookie_key, value, "#{klass_name.underscore}_credentials")
|
|
30
30
|
end
|
|
31
31
|
alias_method :cookie_key=, :cookie_key
|
|
32
32
|
|
|
@@ -35,7 +35,7 @@ module Authlogic
|
|
|
35
35
|
# * <tt>Default:</tt> false
|
|
36
36
|
# * <tt>Accepts:</tt> Boolean
|
|
37
37
|
def remember_me(value = nil)
|
|
38
|
-
|
|
38
|
+
rw_config(:remember_me, value, false)
|
|
39
39
|
end
|
|
40
40
|
alias_method :remember_me=, :remember_me
|
|
41
41
|
|
|
@@ -44,36 +44,38 @@ module Authlogic
|
|
|
44
44
|
# * <tt>Default:</tt> 3.months
|
|
45
45
|
# * <tt>Accepts:</tt> Integer, length of time in seconds, such as 60 or 3.months
|
|
46
46
|
def remember_me_for(value = :_read)
|
|
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
50
|
end
|
|
51
51
|
|
|
52
52
|
# The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
|
|
53
|
-
module InstanceMethods
|
|
53
|
+
module InstanceMethods
|
|
54
|
+
# Allows you to set the remember_me option when passing credentials.
|
|
54
55
|
def credentials=(value)
|
|
55
56
|
super
|
|
56
57
|
values = value.is_a?(Array) ? value : [value]
|
|
57
58
|
case values.first
|
|
58
59
|
when Hash
|
|
59
|
-
self.remember_me = values.first.with_indifferent_access[:remember_me]
|
|
60
|
+
self.remember_me = values.first.with_indifferent_access[:remember_me] if values.first.with_indifferent_access.key?(:remember_me)
|
|
60
61
|
else
|
|
61
62
|
r = values.find { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
|
|
62
63
|
self.remember_me = r if !r.nil?
|
|
63
64
|
end
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
# Is the cookie going to expire after the session is over, or will it stick around?
|
|
68
|
+
def remember_me
|
|
67
69
|
return @remember_me if defined?(@remember_me)
|
|
68
70
|
@remember_me = self.class.remember_me
|
|
69
71
|
end
|
|
70
|
-
|
|
72
|
+
|
|
71
73
|
# Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until".
|
|
72
74
|
def remember_me=(value)
|
|
73
75
|
@remember_me = value
|
|
74
76
|
end
|
|
75
|
-
|
|
76
|
-
#
|
|
77
|
+
|
|
78
|
+
# See remember_me
|
|
77
79
|
def remember_me?
|
|
78
80
|
remember_me == true || remember_me == "true" || remember_me == "1"
|
|
79
81
|
end
|
|
@@ -83,7 +85,7 @@ module Authlogic
|
|
|
83
85
|
return unless remember_me?
|
|
84
86
|
self.class.remember_me_for
|
|
85
87
|
end
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
# When to expire the cookie. See remember_me_for configuration option to change this.
|
|
88
90
|
def remember_me_until
|
|
89
91
|
return unless remember_me?
|
|
@@ -96,27 +98,29 @@ module Authlogic
|
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
def cookie_credentials
|
|
99
|
-
controller.cookies[cookie_key]
|
|
101
|
+
controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::")
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
# Tries to validate the session from information in the cookie
|
|
103
105
|
def persist_by_cookie
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
persistence_token, record_id = cookie_credentials
|
|
107
|
+
if !persistence_token.nil?
|
|
108
|
+
record = record_id.nil? ? search_for_record("find_by_persistence_token", persistence_token) : search_for_record("find_by_#{klass.primary_key}", record_id)
|
|
109
|
+
self.unauthorized_record = record if record && record.persistence_token == persistence_token
|
|
106
110
|
valid?
|
|
107
111
|
else
|
|
108
112
|
false
|
|
109
113
|
end
|
|
110
114
|
end
|
|
111
|
-
|
|
115
|
+
|
|
112
116
|
def save_cookie
|
|
113
117
|
controller.cookies[cookie_key] = {
|
|
114
|
-
:value => record.persistence_token,
|
|
118
|
+
:value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}",
|
|
115
119
|
:expires => remember_me_until,
|
|
116
120
|
:domain => controller.cookie_domain
|
|
117
121
|
}
|
|
118
122
|
end
|
|
119
|
-
|
|
123
|
+
|
|
120
124
|
def destroy_cookie
|
|
121
125
|
controller.cookies.delete cookie_key, :domain => controller.cookie_domain
|
|
122
126
|
end
|
|
@@ -20,7 +20,7 @@ module Authlogic
|
|
|
20
20
|
# A convenince method. The same as:
|
|
21
21
|
#
|
|
22
22
|
# session = UserSession.new(*args)
|
|
23
|
-
# session.
|
|
23
|
+
# session.save
|
|
24
24
|
#
|
|
25
25
|
# Instead you can do:
|
|
26
26
|
#
|
|
@@ -28,12 +28,14 @@ module Authlogic
|
|
|
28
28
|
def create(*args, &block)
|
|
29
29
|
session = new(*args)
|
|
30
30
|
session.save(&block)
|
|
31
|
+
session
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
# Same as create but calls create!, which raises an exception when validation fails.
|
|
34
35
|
def create!(*args)
|
|
35
36
|
session = new(*args)
|
|
36
37
|
session.save!
|
|
38
|
+
session
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
|
|
@@ -42,13 +44,15 @@ module Authlogic
|
|
|
42
44
|
# the user to authenticate again if it is needed.
|
|
43
45
|
def destroy
|
|
44
46
|
before_destroy
|
|
47
|
+
save_record
|
|
45
48
|
errors.clear
|
|
46
49
|
@record = nil
|
|
47
50
|
after_destroy
|
|
48
51
|
true
|
|
49
52
|
end
|
|
50
53
|
|
|
51
|
-
# Returns true if the session has
|
|
54
|
+
# Returns true if the session is new, meaning no action has been taken on it and a successful save
|
|
55
|
+
# has not taken place.
|
|
52
56
|
def new_session?
|
|
53
57
|
new_session != false
|
|
54
58
|
end
|
|
@@ -13,7 +13,7 @@ module Authlogic
|
|
|
13
13
|
|
|
14
14
|
module ClassMethods
|
|
15
15
|
private
|
|
16
|
-
def
|
|
16
|
+
def rw_config(key, value, default_value = nil, read_value = nil)
|
|
17
17
|
if value == read_value
|
|
18
18
|
return read_inheritable_attribute(key) if inheritable_attributes.include?(key)
|
|
19
19
|
write_inheritable_attribute(key, default_value)
|