sorcery 0.8.6 → 0.9.1
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/.gitignore +2 -1
- data/.travis.yml +75 -14
- data/CHANGELOG.md +32 -1
- data/Gemfile +1 -0
- data/README.md +138 -86
- data/gemfiles/active_record-rails40.gemfile +7 -0
- data/gemfiles/active_record-rails41.gemfile +3 -2
- data/gemfiles/mongo_mapper-rails40.gemfile +9 -0
- data/gemfiles/mongo_mapper-rails41.gemfile +2 -1
- data/gemfiles/mongoid-rails40.gemfile +9 -0
- data/gemfiles/mongoid-rails41.gemfile +3 -5
- data/gemfiles/mongoid3-rails32.gemfile +9 -0
- data/lib/generators/sorcery/USAGE +1 -1
- data/lib/generators/sorcery/install_generator.rb +19 -5
- data/lib/generators/sorcery/templates/initializer.rb +36 -10
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -1
- data/lib/generators/sorcery/templates/migration/core.rb +2 -2
- data/lib/generators/sorcery/templates/migration/external.rb +3 -1
- data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
- data/lib/sorcery/adapters/base_adapter.rb +30 -0
- data/lib/sorcery/adapters/data_mapper_adapter.rb +176 -0
- data/lib/sorcery/adapters/mongo_mapper_adapter.rb +110 -0
- data/lib/sorcery/adapters/mongoid_adapter.rb +97 -0
- data/lib/sorcery/controller/config.rb +65 -0
- data/lib/sorcery/controller/submodules/activity_logging.rb +16 -21
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +6 -6
- data/lib/sorcery/controller/submodules/external.rb +9 -29
- data/lib/sorcery/controller/submodules/remember_me.rb +4 -4
- data/lib/sorcery/controller/submodules/session_timeout.rb +10 -6
- data/lib/sorcery/controller.rb +5 -64
- data/lib/sorcery/model/config.rb +96 -0
- data/lib/sorcery/model/submodules/activity_logging.rb +29 -36
- data/lib/sorcery/model/submodules/brute_force_protection.rb +21 -37
- data/lib/sorcery/model/submodules/external.rb +53 -9
- data/lib/sorcery/model/submodules/remember_me.rb +12 -31
- data/lib/sorcery/model/submodules/reset_password.rb +21 -39
- data/lib/sorcery/model/submodules/user_activation.rb +21 -63
- data/lib/sorcery/model/temporary_token.rb +4 -4
- data/lib/sorcery/model.rb +47 -175
- data/lib/sorcery/providers/base.rb +11 -0
- data/lib/sorcery/providers/facebook.rb +16 -5
- data/lib/sorcery/providers/github.rb +11 -2
- data/lib/sorcery/providers/google.rb +1 -1
- data/lib/sorcery/providers/heroku.rb +57 -0
- data/lib/sorcery/providers/jira.rb +77 -0
- data/lib/sorcery/providers/linkedin.rb +1 -1
- data/lib/sorcery/providers/liveid.rb +1 -1
- data/lib/sorcery/providers/salesforce.rb +50 -0
- data/lib/sorcery/providers/twitter.rb +1 -1
- data/lib/sorcery/providers/vk.rb +6 -4
- data/lib/sorcery/providers/xing.rb +2 -2
- data/lib/sorcery/test_helpers/internal.rb +7 -3
- data/lib/sorcery/test_helpers/rails/controller.rb +5 -1
- data/lib/sorcery/version.rb +3 -0
- data/lib/sorcery.rb +75 -43
- data/sorcery.gemspec +6 -2
- data/spec/active_record/user_activity_logging_spec.rb +9 -0
- data/spec/controllers/controller_activity_logging_spec.rb +124 -0
- data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
- data/spec/{active_record → controllers}/controller_http_basic_auth_spec.rb +14 -11
- data/spec/{active_record → controllers}/controller_oauth2_spec.rb +151 -64
- data/spec/{active_record → controllers}/controller_oauth_spec.rb +94 -70
- data/spec/{active_record → controllers}/controller_remember_me_spec.rb +32 -12
- data/spec/{active_record → controllers}/controller_session_timeout_spec.rb +15 -5
- data/spec/{shared_examples/controller_shared_examples.rb → controllers/controller_spec.rb} +34 -19
- data/spec/{datamapper → data_mapper}/user_activation_spec.rb +1 -1
- data/spec/data_mapper/user_activity_logging_spec.rb +14 -0
- data/spec/{datamapper → data_mapper}/user_brute_force_protection_spec.rb +1 -1
- data/spec/{datamapper → data_mapper}/user_oauth_spec.rb +1 -1
- data/spec/{datamapper → data_mapper}/user_remember_me_spec.rb +1 -1
- data/spec/{datamapper → data_mapper}/user_reset_password_spec.rb +1 -1
- data/spec/{datamapper → data_mapper}/user_spec.rb +1 -1
- data/spec/mongoid/user_spec.rb +13 -0
- data/spec/orm/active_record.rb +12 -0
- data/spec/orm/{datamapper.rb → data_mapper.rb} +16 -2
- data/spec/orm/mongo_mapper.rb +0 -1
- data/spec/orm/mongoid.rb +4 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +62 -1
- data/spec/rails_app/app/mongo_mapper/user.rb +2 -0
- data/spec/rails_app/config/routes.rb +9 -0
- data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +2 -2
- data/spec/shared_examples/user_activation_shared_examples.rb +7 -7
- data/spec/shared_examples/user_activity_logging_shared_examples.rb +73 -5
- data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +127 -9
- data/spec/shared_examples/user_oauth_shared_examples.rb +3 -6
- data/spec/shared_examples/user_remember_me_shared_examples.rb +6 -3
- data/spec/shared_examples/user_reset_password_shared_examples.rb +10 -10
- data/spec/shared_examples/user_shared_examples.rb +146 -46
- data/spec/spec_helper.rb +7 -22
- metadata +36 -57
- data/Gemfile.rails4 +0 -22
- data/VERSION +0 -1
- data/lib/sorcery/model/adapters/active_record.rb +0 -54
- data/lib/sorcery/model/adapters/datamapper.rb +0 -123
- data/lib/sorcery/model/adapters/mongo_mapper.rb +0 -60
- data/lib/sorcery/model/adapters/mongoid.rb +0 -88
- data/lib/sorcery/test_helpers/rails.rb +0 -7
- data/spec/active_record/controller_activity_logging_spec.rb +0 -29
- data/spec/active_record/controller_brute_force_protection_spec.rb +0 -158
- data/spec/active_record/controller_spec.rb +0 -8
- data/spec/active_record/integration_spec.rb +0 -23
- data/spec/datamapper/controller_activity_logging_spec.rb +0 -17
- data/spec/datamapper/controller_spec.rb +0 -8
- data/spec/datamapper/user_activity_logging_spec.rb +0 -9
- data/spec/mongo_mapper/controller_spec.rb +0 -8
- data/spec/mongoid/controller_activity_logging_spec.rb +0 -16
- data/spec/mongoid/controller_spec.rb +0 -8
- data/spec/rails_app/public/404.html +0 -26
- data/spec/rails_app/public/422.html +0 -26
- data/spec/rails_app/public/500.html +0 -26
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/javascripts/application.js +0 -2
- data/spec/rails_app/public/javascripts/controls.js +0 -965
- data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
- data/spec/rails_app/public/javascripts/effects.js +0 -1123
- data/spec/rails_app/public/javascripts/prototype.js +0 -6001
- data/spec/rails_app/public/javascripts/rails.js +0 -175
- data/spec/rails_app/public/robots.txt +0 -5
- data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
- data/spec/shared_examples/controller_activity_logging_shared_examples.rb +0 -125
- data/spec/shared_examples/controller_oauth2_shared_examples.rb +0 -52
- data/spec/shared_examples/controller_oauth_shared_examples.rb +0 -62
- /data/spec/rails_app/app/{datamapper → data_mapper}/authentication.rb +0 -0
- /data/spec/rails_app/app/{datamapper → data_mapper}/user.rb +0 -0
|
@@ -3,22 +3,23 @@ module Sorcery
|
|
|
3
3
|
module Submodules
|
|
4
4
|
# This submodule keeps track of events such as login, logout, and last activity time, per user.
|
|
5
5
|
# It helps in estimating which users are active now in the site.
|
|
6
|
-
# This cannot be determined absolutely because a user might be reading a page without clicking anything
|
|
6
|
+
# This cannot be determined absolutely because a user might be reading a page without clicking anything
|
|
7
7
|
# for a while.
|
|
8
8
|
# This is the model part of the submodule, which provides configuration options.
|
|
9
9
|
module ActivityLogging
|
|
10
10
|
def self.included(base)
|
|
11
11
|
base.extend(ClassMethods)
|
|
12
|
+
base.send(:include, InstanceMethods)
|
|
12
13
|
|
|
13
14
|
base.sorcery_config.class_eval do
|
|
14
15
|
attr_accessor :last_login_at_attribute_name, # last login attribute name.
|
|
15
16
|
:last_logout_at_attribute_name, # last logout attribute name.
|
|
16
17
|
:last_activity_at_attribute_name, # last activity attribute name.
|
|
17
18
|
:last_login_from_ip_address_name, # last activity login source
|
|
18
|
-
:activity_timeout # how long since last activity is
|
|
19
|
+
:activity_timeout # how long since last activity is
|
|
19
20
|
#the user defined logged out?
|
|
20
21
|
end
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
base.sorcery_config.instance_eval do
|
|
23
24
|
@defaults.merge!(:@last_login_at_attribute_name => :last_login_at,
|
|
24
25
|
:@last_logout_at_attribute_name => :last_logout_at,
|
|
@@ -28,47 +29,39 @@ module Sorcery
|
|
|
28
29
|
reset!
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
base.sorcery_config.after_config << :
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
base.sorcery_config.after_config << :define_activity_logging_fields
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module InstanceMethods
|
|
36
|
+
def set_last_login_at(time)
|
|
37
|
+
sorcery_adapter.update_attribute(sorcery_config.last_login_at_attribute_name, time)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set_last_logout_at(time)
|
|
41
|
+
sorcery_adapter.update_attribute(sorcery_config.last_logout_at_attribute_name, time)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def set_last_activity_at(time)
|
|
45
|
+
sorcery_adapter.update_attribute(sorcery_config.last_activity_at_attribute_name, time)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def set_last_ip_addess(ip_address)
|
|
49
|
+
sorcery_adapter.update_attribute(sorcery_config.last_login_from_ip_address_name, ip_address)
|
|
38
50
|
end
|
|
39
51
|
end
|
|
40
|
-
|
|
52
|
+
|
|
41
53
|
module ClassMethods
|
|
42
54
|
# get all users with last_activity within timeout
|
|
43
55
|
def current_users
|
|
44
|
-
|
|
45
|
-
get_current_users
|
|
56
|
+
sorcery_adapter.get_current_users
|
|
46
57
|
end
|
|
47
58
|
|
|
48
59
|
protected
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
field sorcery_config.last_login_from_ip_address_name, :type => String
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def define_activity_logging_datamapper_fields
|
|
58
|
-
property sorcery_config.last_login_at_attribute_name, Time
|
|
59
|
-
property sorcery_config.last_logout_at_attribute_name, Time
|
|
60
|
-
property sorcery_config.last_activity_at_attribute_name, Time
|
|
61
|
-
property sorcery_config.last_login_from_ip_address_name, String
|
|
62
|
-
# Workaround local timezone retrieval problem NOTE dm-core issue #193
|
|
63
|
-
[sorcery_config.last_login_at_attribute_name,
|
|
64
|
-
sorcery_config.last_logout_at_attribute_name,
|
|
65
|
-
sorcery_config.last_activity_at_attribute_name].each do |sym|
|
|
66
|
-
alias_method "orig_#{sym}", sym
|
|
67
|
-
define_method(sym) do
|
|
68
|
-
t = send("orig_#{sym}")
|
|
69
|
-
t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
60
|
+
def define_activity_logging_fields
|
|
61
|
+
sorcery_adapter.define_field sorcery_config.last_login_at_attribute_name, Time
|
|
62
|
+
sorcery_adapter.define_field sorcery_config.last_logout_at_attribute_name, Time
|
|
63
|
+
sorcery_adapter.define_field sorcery_config.last_activity_at_attribute_name, Time
|
|
64
|
+
sorcery_adapter.define_field sorcery_config.last_login_from_ip_address_name, String
|
|
72
65
|
end
|
|
73
66
|
end
|
|
74
67
|
end
|
|
@@ -35,13 +35,7 @@ module Sorcery
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
base.sorcery_config.before_authenticate << :prevent_locked_user_login
|
|
38
|
-
base.sorcery_config.after_config << :
|
|
39
|
-
if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
|
|
40
|
-
base.sorcery_config.after_config << :define_brute_force_protection_mongo_mapper_fields
|
|
41
|
-
end
|
|
42
|
-
if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
|
|
43
|
-
base.sorcery_config.after_config << :define_brute_force_protection_datamapper_fields
|
|
44
|
-
end
|
|
38
|
+
base.sorcery_config.after_config << :define_brute_force_protection_fields
|
|
45
39
|
base.extend(ClassMethods)
|
|
46
40
|
base.send(:include, InstanceMethods)
|
|
47
41
|
end
|
|
@@ -49,35 +43,16 @@ module Sorcery
|
|
|
49
43
|
module ClassMethods
|
|
50
44
|
def load_from_unlock_token(token)
|
|
51
45
|
return nil if token.blank?
|
|
52
|
-
user =
|
|
46
|
+
user = sorcery_adapter.find_by_token(sorcery_config.unlock_token_attribute_name,token)
|
|
53
47
|
user
|
|
54
48
|
end
|
|
55
49
|
|
|
56
50
|
protected
|
|
57
51
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def define_brute_force_protection_mongo_mapper_fields
|
|
65
|
-
key sorcery_config.failed_logins_count_attribute_name, Integer, :default => 0
|
|
66
|
-
key sorcery_config.lock_expires_at_attribute_name, Time
|
|
67
|
-
key sorcery_config.unlock_token_attribute_name, String
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def define_brute_force_protection_datamapper_fields
|
|
71
|
-
property sorcery_config.failed_logins_count_attribute_name, Integer, :default => 0
|
|
72
|
-
property sorcery_config.lock_expires_at_attribute_name, Time
|
|
73
|
-
property sorcery_config.unlock_token_attribute_name, String
|
|
74
|
-
[sorcery_config.lock_expires_at_attribute_name].each do |sym|
|
|
75
|
-
alias_method "orig_#{sym}", sym
|
|
76
|
-
define_method(sym) do
|
|
77
|
-
t = send("orig_#{sym}")
|
|
78
|
-
t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
52
|
+
def define_brute_force_protection_fields
|
|
53
|
+
sorcery_adapter.define_field sorcery_config.failed_logins_count_attribute_name, Integer, :default => 0
|
|
54
|
+
sorcery_adapter.define_field sorcery_config.lock_expires_at_attribute_name, Time
|
|
55
|
+
sorcery_adapter.define_field sorcery_config.unlock_token_attribute_name, String
|
|
81
56
|
end
|
|
82
57
|
end
|
|
83
58
|
|
|
@@ -87,9 +62,12 @@ module Sorcery
|
|
|
87
62
|
def register_failed_login!
|
|
88
63
|
config = sorcery_config
|
|
89
64
|
return if !unlocked?
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
|
|
66
|
+
sorcery_adapter.increment(config.failed_logins_count_attribute_name)
|
|
67
|
+
|
|
68
|
+
if self.send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit
|
|
69
|
+
lock!
|
|
70
|
+
end
|
|
93
71
|
end
|
|
94
72
|
|
|
95
73
|
# /!\
|
|
@@ -100,7 +78,11 @@ module Sorcery
|
|
|
100
78
|
attributes = {config.lock_expires_at_attribute_name => nil,
|
|
101
79
|
config.failed_logins_count_attribute_name => 0,
|
|
102
80
|
config.unlock_token_attribute_name => nil}
|
|
103
|
-
|
|
81
|
+
sorcery_adapter.update_attributes(attributes)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def locked?
|
|
85
|
+
!unlocked?
|
|
104
86
|
end
|
|
105
87
|
|
|
106
88
|
protected
|
|
@@ -109,7 +91,7 @@ module Sorcery
|
|
|
109
91
|
config = sorcery_config
|
|
110
92
|
attributes = {config.lock_expires_at_attribute_name => Time.now.in_time_zone + config.login_lock_time_period,
|
|
111
93
|
config.unlock_token_attribute_name => TemporaryToken.generate_random_token}
|
|
112
|
-
|
|
94
|
+
sorcery_adapter.update_attributes(attributes)
|
|
113
95
|
|
|
114
96
|
unless config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil?
|
|
115
97
|
send_unlock_token_email!
|
|
@@ -122,7 +104,9 @@ module Sorcery
|
|
|
122
104
|
end
|
|
123
105
|
|
|
124
106
|
def send_unlock_token_email!
|
|
125
|
-
|
|
107
|
+
return if sorcery_config.unlock_token_email_method_name.nil?
|
|
108
|
+
|
|
109
|
+
generic_send_email(:unlock_token_email_method_name, :unlock_token_mailer)
|
|
126
110
|
end
|
|
127
111
|
|
|
128
112
|
# Prevents a locked user from logging in, and unlocks users that expired their lock time.
|
|
@@ -8,7 +8,7 @@ module Sorcery
|
|
|
8
8
|
# Socery assumes (read: requires) you will create external users in the same table where
|
|
9
9
|
# you keep your regular users,
|
|
10
10
|
# but that you will have a separate table for keeping their external authentication data,
|
|
11
|
-
# and that that separate table has a few rows for each user, facebook and twitter
|
|
11
|
+
# and that that separate table has a few rows for each user, facebook and twitter
|
|
12
12
|
# for example (a one-to-many relationship).
|
|
13
13
|
#
|
|
14
14
|
# External users will have a null crypted_password field, since we do not hold their password.
|
|
@@ -22,7 +22,7 @@ module Sorcery
|
|
|
22
22
|
:provider_uid_attribute_name
|
|
23
23
|
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
base.sorcery_config.instance_eval do
|
|
27
27
|
@defaults.merge!(:@authentications_class => nil,
|
|
28
28
|
:@authentications_user_id_attribute_name => :user_id,
|
|
@@ -31,26 +31,70 @@ module Sorcery
|
|
|
31
31
|
|
|
32
32
|
reset!
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
base.send(:include, InstanceMethods)
|
|
36
36
|
base.extend(ClassMethods)
|
|
37
37
|
|
|
38
38
|
end
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
module ClassMethods
|
|
41
41
|
# takes a provider and uid and finds a user by them.
|
|
42
42
|
def load_from_provider(provider,uid)
|
|
43
43
|
config = sorcery_config
|
|
44
|
-
authentication = config.authentications_class.
|
|
45
|
-
user =
|
|
44
|
+
authentication = config.authentications_class.sorcery_adapter.find_by_oauth_credentials(provider, uid)
|
|
45
|
+
user = sorcery_adapter.find_by_id(authentication.send(config.authentications_user_id_attribute_name)) if authentication
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_and_validate_from_provider(provider, uid, attrs)
|
|
49
|
+
user = new(attrs)
|
|
50
|
+
user.send(sorcery_config.authentications_class.to_s.downcase.pluralize).build(
|
|
51
|
+
sorcery_config.provider_uid_attribute_name => uid,
|
|
52
|
+
sorcery_config.provider_attribute_name => provider
|
|
53
|
+
)
|
|
54
|
+
saved = user.sorcery_adapter.save
|
|
55
|
+
[user, saved]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_from_provider(provider, uid, attrs)
|
|
59
|
+
user = new
|
|
60
|
+
attrs.each do |k,v|
|
|
61
|
+
user.send(:"#{k}=", v)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if block_given?
|
|
65
|
+
return false unless yield user
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sorcery_adapter.transaction do
|
|
69
|
+
user.sorcery_adapter.save(:validate => false)
|
|
70
|
+
sorcery_config.authentications_class.create!(
|
|
71
|
+
sorcery_config.authentications_user_id_attribute_name => user.id,
|
|
72
|
+
sorcery_config.provider_attribute_name => provider,
|
|
73
|
+
sorcery_config.provider_uid_attribute_name => uid
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
user
|
|
46
77
|
end
|
|
47
78
|
end
|
|
48
|
-
|
|
79
|
+
|
|
49
80
|
module InstanceMethods
|
|
81
|
+
def add_provider_to_user(provider, uid)
|
|
82
|
+
authentications = sorcery_config.authentications_class.name.underscore.pluralize
|
|
83
|
+
# first check to see if user has a particular authentication already
|
|
84
|
+
if sorcery_adapter.find_authentication_by_oauth_credentials(authentications, provider, uid).nil?
|
|
85
|
+
user = send(authentications).build(sorcery_config.provider_uid_attribute_name => uid,
|
|
86
|
+
sorcery_config.provider_attribute_name => provider)
|
|
87
|
+
user.sorcery_adapter.save(validate: false)
|
|
88
|
+
else
|
|
89
|
+
user = false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
user
|
|
93
|
+
end
|
|
50
94
|
|
|
51
95
|
end
|
|
52
|
-
|
|
96
|
+
|
|
53
97
|
end
|
|
54
98
|
end
|
|
55
99
|
end
|
|
56
|
-
end
|
|
100
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sorcery
|
|
2
2
|
module Model
|
|
3
3
|
module Submodules
|
|
4
|
-
# The Remember Me submodule takes care of setting the user's cookie so that he will
|
|
4
|
+
# The Remember Me submodule takes care of setting the user's cookie so that he will
|
|
5
5
|
# be automatically logged in to the site on every visit,
|
|
6
6
|
# until the cookie expires.
|
|
7
7
|
module RememberMe
|
|
@@ -22,14 +22,7 @@ module Sorcery
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
base.send(:include, InstanceMethods)
|
|
25
|
-
|
|
26
|
-
base.sorcery_config.after_config << :define_remember_me_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
|
|
27
|
-
if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
|
|
28
|
-
base.sorcery_config.after_config << :define_remember_me_mongo_mapper_fields
|
|
29
|
-
end
|
|
30
|
-
if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
|
|
31
|
-
base.sorcery_config.after_config << :define_remember_me_datamapper_fields
|
|
32
|
-
end
|
|
25
|
+
base.sorcery_config.after_config << :define_remember_me_fields
|
|
33
26
|
|
|
34
27
|
base.extend(ClassMethods)
|
|
35
28
|
end
|
|
@@ -37,41 +30,29 @@ module Sorcery
|
|
|
37
30
|
module ClassMethods
|
|
38
31
|
protected
|
|
39
32
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def define_remember_me_mongo_mapper_fields
|
|
46
|
-
key sorcery_config.remember_me_token_attribute_name, String
|
|
47
|
-
key sorcery_config.remember_me_token_expires_at_attribute_name, Time
|
|
33
|
+
def define_remember_me_fields
|
|
34
|
+
sorcery_adapter.define_field sorcery_config.remember_me_token_attribute_name, String
|
|
35
|
+
sorcery_adapter.define_field sorcery_config.remember_me_token_expires_at_attribute_name, Time
|
|
48
36
|
end
|
|
49
37
|
|
|
50
|
-
def define_remember_me_datamapper_fields
|
|
51
|
-
property sorcery_config.remember_me_token_attribute_name, String
|
|
52
|
-
property sorcery_config.remember_me_token_expires_at_attribute_name, Time
|
|
53
|
-
[sorcery_config.remember_me_token_expires_at_attribute_name].each do |sym|
|
|
54
|
-
alias_method "orig_#{sym}", sym
|
|
55
|
-
define_method(sym) do
|
|
56
|
-
t = send("orig_#{sym}")
|
|
57
|
-
t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
38
|
end
|
|
62
39
|
|
|
63
40
|
module InstanceMethods
|
|
64
41
|
# You shouldn't really use this one yourself - it's called by the controller's 'remember_me!' method.
|
|
65
42
|
def remember_me!
|
|
66
43
|
config = sorcery_config
|
|
67
|
-
self.
|
|
44
|
+
self.sorcery_adapter.update_attributes(config.remember_me_token_attribute_name => TemporaryToken.generate_random_token,
|
|
68
45
|
config.remember_me_token_expires_at_attribute_name => Time.now.in_time_zone + config.remember_me_for)
|
|
69
46
|
end
|
|
70
|
-
|
|
47
|
+
|
|
48
|
+
def has_remember_me_token?
|
|
49
|
+
self.send(sorcery_config.remember_me_token_attribute_name).present?
|
|
50
|
+
end
|
|
51
|
+
|
|
71
52
|
# You shouldn't really use this one yourself - it's called by the controller's 'forget_me!' method.
|
|
72
53
|
def forget_me!
|
|
73
54
|
config = sorcery_config
|
|
74
|
-
self.
|
|
55
|
+
self.sorcery_adapter.update_attributes(config.remember_me_token_attribute_name => nil,
|
|
75
56
|
config.remember_me_token_expires_at_attribute_name => nil)
|
|
76
57
|
end
|
|
77
58
|
end
|
|
@@ -50,13 +50,7 @@ module Sorcery
|
|
|
50
50
|
base.extend(ClassMethods)
|
|
51
51
|
|
|
52
52
|
base.sorcery_config.after_config << :validate_mailer_defined
|
|
53
|
-
base.sorcery_config.after_config << :
|
|
54
|
-
if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
|
|
55
|
-
base.sorcery_config.after_config << :define_reset_password_mongo_mapper_fields
|
|
56
|
-
end
|
|
57
|
-
if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
|
|
58
|
-
base.sorcery_config.after_config << :define_reset_password_datamapper_fields
|
|
59
|
-
end
|
|
53
|
+
base.sorcery_config.after_config << :define_reset_password_fields
|
|
60
54
|
|
|
61
55
|
base.send(:include, InstanceMethods)
|
|
62
56
|
|
|
@@ -80,45 +74,33 @@ module Sorcery
|
|
|
80
74
|
raise ArgumentError, msg if @sorcery_config.reset_password_mailer == nil and @sorcery_config.reset_password_mailer_disabled == false
|
|
81
75
|
end
|
|
82
76
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
def define_reset_password_fields
|
|
78
|
+
sorcery_adapter.define_field sorcery_config.reset_password_token_attribute_name, String
|
|
79
|
+
sorcery_adapter.define_field sorcery_config.reset_password_token_expires_at_attribute_name, Time
|
|
80
|
+
sorcery_adapter.define_field sorcery_config.reset_password_email_sent_at_attribute_name, Time
|
|
87
81
|
end
|
|
88
82
|
|
|
89
|
-
def define_reset_password_mongo_mapper_fields
|
|
90
|
-
key sorcery_config.reset_password_token_attribute_name, String
|
|
91
|
-
key sorcery_config.reset_password_token_expires_at_attribute_name, Time
|
|
92
|
-
key sorcery_config.reset_password_email_sent_at_attribute_name, Time
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def define_reset_password_datamapper_fields
|
|
96
|
-
property sorcery_config.reset_password_token_attribute_name, String
|
|
97
|
-
property sorcery_config.reset_password_token_expires_at_attribute_name, Time
|
|
98
|
-
property sorcery_config.reset_password_email_sent_at_attribute_name, Time
|
|
99
|
-
[sorcery_config.reset_password_token_expires_at_attribute_name,
|
|
100
|
-
sorcery_config.reset_password_email_sent_at_attribute_name].each do |sym|
|
|
101
|
-
alias_method "orig_#{sym}", sym
|
|
102
|
-
define_method(sym) do
|
|
103
|
-
t = send("orig_#{sym}")
|
|
104
|
-
t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
83
|
end
|
|
109
84
|
|
|
110
85
|
module InstanceMethods
|
|
111
|
-
# generates a reset code with expiration
|
|
112
|
-
def
|
|
86
|
+
# generates a reset code with expiration
|
|
87
|
+
def generate_reset_password_token!
|
|
113
88
|
config = sorcery_config
|
|
114
|
-
# hammering protection
|
|
115
|
-
return false if config.reset_password_time_between_emails && self.send(config.reset_password_email_sent_at_attribute_name) && self.send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.ago.utc
|
|
116
89
|
attributes = {config.reset_password_token_attribute_name => TemporaryToken.generate_random_token,
|
|
117
90
|
config.reset_password_email_sent_at_attribute_name => Time.now.in_time_zone}
|
|
118
91
|
attributes[config.reset_password_token_expires_at_attribute_name] = Time.now.in_time_zone + config.reset_password_expiration_period if config.reset_password_expiration_period
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
92
|
+
|
|
93
|
+
self.sorcery_adapter.update_attributes(attributes)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# generates a reset code with expiration and sends an email to the user.
|
|
97
|
+
def deliver_reset_password_instructions!
|
|
98
|
+
config = sorcery_config
|
|
99
|
+
# hammering protection
|
|
100
|
+
return false if config.reset_password_time_between_emails.present? && self.send(config.reset_password_email_sent_at_attribute_name) && self.send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.seconds.ago.utc
|
|
101
|
+
self.class.sorcery_adapter.transaction do
|
|
102
|
+
generate_reset_password_token!
|
|
103
|
+
send_reset_password_email! unless config.reset_password_mailer_disabled
|
|
122
104
|
end
|
|
123
105
|
end
|
|
124
106
|
|
|
@@ -126,11 +108,11 @@ module Sorcery
|
|
|
126
108
|
def change_password!(new_password)
|
|
127
109
|
clear_reset_password_token
|
|
128
110
|
self.send(:"#{sorcery_config.password_attribute_name}=", new_password)
|
|
129
|
-
|
|
111
|
+
sorcery_adapter.save
|
|
130
112
|
end
|
|
131
113
|
|
|
132
114
|
protected
|
|
133
|
-
|
|
115
|
+
|
|
134
116
|
def send_reset_password_email!
|
|
135
117
|
generic_send_email(:reset_password_email_method_name, :reset_password_mailer)
|
|
136
118
|
end
|
|
@@ -15,7 +15,7 @@ module Sorcery
|
|
|
15
15
|
# (sent by email).
|
|
16
16
|
|
|
17
17
|
:activation_token_expires_at_attribute_name, # the attribute name to hold activation code
|
|
18
|
-
# expiration date.
|
|
18
|
+
# expiration date.
|
|
19
19
|
|
|
20
20
|
:activation_token_expiration_period, # how many seconds before the activation code
|
|
21
21
|
# expires. nil for never expires.
|
|
@@ -51,33 +51,14 @@ module Sorcery
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
base.class_eval do
|
|
54
|
-
if
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
after :create do
|
|
61
|
-
if send_activation_needed_email?
|
|
62
|
-
send_activation_needed_email!
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
else
|
|
66
|
-
# don't setup activation if no password supplied - this user is created automatically
|
|
67
|
-
before_create :setup_activation, :if => Proc.new { |user| user.send(sorcery_config.password_attribute_name).present? }
|
|
68
|
-
# don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
|
|
69
|
-
after_create :send_activation_needed_email!, :if => :send_activation_needed_email?
|
|
70
|
-
end
|
|
54
|
+
# don't setup activation if no password supplied - this user is created automatically
|
|
55
|
+
sorcery_adapter.define_callback :before, :create, :setup_activation, :if => Proc.new { |user| user.send(sorcery_config.password_attribute_name).present? }
|
|
56
|
+
# don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
|
|
57
|
+
sorcery_adapter.define_callback :after, :create, :send_activation_needed_email!, :if => :send_activation_needed_email?
|
|
71
58
|
end
|
|
72
59
|
|
|
73
60
|
base.sorcery_config.after_config << :validate_mailer_defined
|
|
74
|
-
base.sorcery_config.after_config << :
|
|
75
|
-
if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
|
|
76
|
-
base.sorcery_config.after_config << :define_user_activation_mongo_mapper_fields
|
|
77
|
-
end
|
|
78
|
-
if defined?(DataMapper) and base.ancestors.include?(DataMapper::Resource)
|
|
79
|
-
base.sorcery_config.after_config << :define_user_activation_datamapper_fields
|
|
80
|
-
end
|
|
61
|
+
base.sorcery_config.after_config << :define_user_activation_fields
|
|
81
62
|
base.sorcery_config.before_authenticate << :prevent_non_active_login
|
|
82
63
|
|
|
83
64
|
base.extend(ClassMethods)
|
|
@@ -104,58 +85,35 @@ module Sorcery
|
|
|
104
85
|
raise ArgumentError, msg if @sorcery_config.user_activation_mailer == nil and @sorcery_config.activation_mailer_disabled == false
|
|
105
86
|
end
|
|
106
87
|
|
|
107
|
-
def
|
|
108
|
-
self.class_eval do
|
|
109
|
-
field sorcery_config.activation_state_attribute_name, :type => String
|
|
110
|
-
field sorcery_config.activation_token_attribute_name, :type => String
|
|
111
|
-
field sorcery_config.activation_token_expires_at_attribute_name, :type => Time
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def define_user_activation_mongo_mapper_fields
|
|
88
|
+
def define_user_activation_fields
|
|
116
89
|
self.class_eval do
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def define_user_activation_datamapper_fields
|
|
124
|
-
self.class_eval do
|
|
125
|
-
property sorcery_config.activation_state_attribute_name, String
|
|
126
|
-
property sorcery_config.activation_token_attribute_name, String
|
|
127
|
-
property sorcery_config.activation_token_expires_at_attribute_name, Time
|
|
128
|
-
[sorcery_config.activation_token_expires_at_attribute_name].each do |sym|
|
|
129
|
-
alias_method "orig_#{sym}", sym
|
|
130
|
-
define_method(sym) do
|
|
131
|
-
t = send("orig_#{sym}")
|
|
132
|
-
t && Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
90
|
+
sorcery_adapter.define_field sorcery_config.activation_state_attribute_name, String
|
|
91
|
+
sorcery_adapter.define_field sorcery_config.activation_token_attribute_name, String
|
|
92
|
+
sorcery_adapter.define_field sorcery_config.activation_token_expires_at_attribute_name, Time
|
|
135
93
|
end
|
|
136
94
|
end
|
|
137
95
|
end
|
|
138
96
|
|
|
139
97
|
module InstanceMethods
|
|
98
|
+
def setup_activation
|
|
99
|
+
config = sorcery_config
|
|
100
|
+
generated_activation_token = TemporaryToken.generate_random_token
|
|
101
|
+
self.send(:"#{config.activation_token_attribute_name}=", generated_activation_token)
|
|
102
|
+
self.send(:"#{config.activation_state_attribute_name}=", "pending")
|
|
103
|
+
self.send(:"#{config.activation_token_expires_at_attribute_name}=", Time.now.in_time_zone + config.activation_token_expiration_period) if config.activation_token_expiration_period
|
|
104
|
+
end
|
|
105
|
+
|
|
140
106
|
# clears activation code, sets the user as 'active' and optionaly sends a success email.
|
|
141
107
|
def activate!
|
|
142
108
|
config = sorcery_config
|
|
143
109
|
self.send(:"#{config.activation_token_attribute_name}=", nil)
|
|
144
110
|
self.send(:"#{config.activation_state_attribute_name}=", "active")
|
|
145
111
|
send_activation_success_email! if send_activation_success_email?
|
|
146
|
-
|
|
112
|
+
sorcery_adapter.save(:validate => false, :raise_on_failure => true)
|
|
147
113
|
end
|
|
148
114
|
|
|
149
115
|
protected
|
|
150
116
|
|
|
151
|
-
def setup_activation
|
|
152
|
-
config = sorcery_config
|
|
153
|
-
generated_activation_token = TemporaryToken.generate_random_token
|
|
154
|
-
self.send(:"#{config.activation_token_attribute_name}=", generated_activation_token)
|
|
155
|
-
self.send(:"#{config.activation_state_attribute_name}=", "pending")
|
|
156
|
-
self.send(:"#{config.activation_token_expires_at_attribute_name}=", Time.now.in_time_zone + config.activation_token_expiration_period) if config.activation_token_expiration_period
|
|
157
|
-
end
|
|
158
|
-
|
|
159
117
|
# called automatically after user initial creation.
|
|
160
118
|
def send_activation_needed_email!
|
|
161
119
|
generic_send_email(:activation_needed_email_method_name, :user_activation_mailer)
|
|
@@ -164,14 +122,14 @@ module Sorcery
|
|
|
164
122
|
def send_activation_success_email!
|
|
165
123
|
generic_send_email(:activation_success_email_method_name, :user_activation_mailer)
|
|
166
124
|
end
|
|
167
|
-
|
|
125
|
+
|
|
168
126
|
def send_activation_success_email?
|
|
169
127
|
!external? && (
|
|
170
128
|
!(sorcery_config.activation_success_email_method_name.nil? ||
|
|
171
129
|
sorcery_config.activation_mailer_disabled == true)
|
|
172
130
|
)
|
|
173
131
|
end
|
|
174
|
-
|
|
132
|
+
|
|
175
133
|
def send_activation_needed_email?
|
|
176
134
|
!external? && (
|
|
177
135
|
!(sorcery_config.activation_needed_email_method_name.nil? ||
|
|
@@ -3,22 +3,22 @@ require 'securerandom'
|
|
|
3
3
|
module Sorcery
|
|
4
4
|
module Model
|
|
5
5
|
# This module encapsulates the logic for temporary token.
|
|
6
|
-
# A temporary token is created to identify a user in scenarios
|
|
6
|
+
# A temporary token is created to identify a user in scenarios
|
|
7
7
|
# such as reseting password and activating the user by email.
|
|
8
8
|
module TemporaryToken
|
|
9
9
|
def self.included(base)
|
|
10
10
|
base.extend(ClassMethods)
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
# Random code, used for salt and temporary tokens.
|
|
14
14
|
def self.generate_random_token
|
|
15
15
|
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
module ClassMethods
|
|
19
19
|
def load_from_token(token, token_attr_name, token_expiration_date_attr)
|
|
20
20
|
return nil if token.blank?
|
|
21
|
-
user =
|
|
21
|
+
user = sorcery_adapter.find_by_token(token_attr_name,token)
|
|
22
22
|
if !user.blank? && !user.send(token_expiration_date_attr).nil?
|
|
23
23
|
return Time.now.in_time_zone < user.send(token_expiration_date_attr) ? user : nil
|
|
24
24
|
end
|