cbsorcery 0.8.6
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/.document +5 -0
- data/.gitignore +56 -0
- data/.rspec +1 -0
- data/.travis.yml +40 -0
- data/CHANGELOG.md +263 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +360 -0
- data/Rakefile +6 -0
- data/gemfiles/active_record-rails40.gemfile +7 -0
- data/gemfiles/active_record-rails41.gemfile +7 -0
- data/lib/generators/sorcery/USAGE +22 -0
- data/lib/generators/sorcery/helpers.rb +40 -0
- data/lib/generators/sorcery/install_generator.rb +95 -0
- data/lib/generators/sorcery/templates/initializer.rb +451 -0
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +10 -0
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +9 -0
- data/lib/generators/sorcery/templates/migration/core.rb +13 -0
- data/lib/generators/sorcery/templates/migration/external.rb +12 -0
- data/lib/generators/sorcery/templates/migration/remember_me.rb +8 -0
- data/lib/generators/sorcery/templates/migration/reset_password.rb +9 -0
- data/lib/generators/sorcery/templates/migration/user_activation.rb +9 -0
- data/lib/sorcery.rb +85 -0
- data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
- data/lib/sorcery/adapters/base_adapter.rb +30 -0
- data/lib/sorcery/controller.rb +157 -0
- data/lib/sorcery/controller/config.rb +65 -0
- data/lib/sorcery/controller/submodules/activity_logging.rb +82 -0
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +38 -0
- data/lib/sorcery/controller/submodules/external.rb +199 -0
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +74 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +81 -0
- data/lib/sorcery/controller/submodules/session_timeout.rb +56 -0
- data/lib/sorcery/crypto_providers/aes256.rb +51 -0
- data/lib/sorcery/crypto_providers/bcrypt.rb +97 -0
- data/lib/sorcery/crypto_providers/common.rb +35 -0
- data/lib/sorcery/crypto_providers/md5.rb +19 -0
- data/lib/sorcery/crypto_providers/sha1.rb +28 -0
- data/lib/sorcery/crypto_providers/sha256.rb +36 -0
- data/lib/sorcery/crypto_providers/sha512.rb +36 -0
- data/lib/sorcery/engine.rb +21 -0
- data/lib/sorcery/model.rb +183 -0
- data/lib/sorcery/model/config.rb +96 -0
- data/lib/sorcery/model/submodules/activity_logging.rb +70 -0
- data/lib/sorcery/model/submodules/brute_force_protection.rb +125 -0
- data/lib/sorcery/model/submodules/external.rb +100 -0
- data/lib/sorcery/model/submodules/remember_me.rb +62 -0
- data/lib/sorcery/model/submodules/reset_password.rb +131 -0
- data/lib/sorcery/model/submodules/user_activation.rb +149 -0
- data/lib/sorcery/model/temporary_token.rb +30 -0
- data/lib/sorcery/protocols/certs/ca-bundle.crt +5182 -0
- data/lib/sorcery/protocols/oauth.rb +42 -0
- data/lib/sorcery/protocols/oauth2.rb +47 -0
- data/lib/sorcery/providers/base.rb +27 -0
- data/lib/sorcery/providers/facebook.rb +63 -0
- data/lib/sorcery/providers/github.rb +51 -0
- data/lib/sorcery/providers/google.rb +51 -0
- data/lib/sorcery/providers/jira.rb +77 -0
- data/lib/sorcery/providers/linkedin.rb +66 -0
- data/lib/sorcery/providers/liveid.rb +53 -0
- data/lib/sorcery/providers/twitter.rb +59 -0
- data/lib/sorcery/providers/vk.rb +63 -0
- data/lib/sorcery/providers/xing.rb +64 -0
- data/lib/sorcery/railties/tasks.rake +6 -0
- data/lib/sorcery/test_helpers/internal.rb +78 -0
- data/lib/sorcery/test_helpers/internal/rails.rb +68 -0
- data/lib/sorcery/test_helpers/rails/controller.rb +21 -0
- data/lib/sorcery/test_helpers/rails/integration.rb +26 -0
- data/lib/sorcery/version.rb +3 -0
- data/sorcery.gemspec +34 -0
- data/spec/active_record/user_activation_spec.rb +18 -0
- data/spec/active_record/user_activity_logging_spec.rb +17 -0
- data/spec/active_record/user_brute_force_protection_spec.rb +16 -0
- data/spec/active_record/user_oauth_spec.rb +16 -0
- data/spec/active_record/user_remember_me_spec.rb +16 -0
- data/spec/active_record/user_reset_password_spec.rb +16 -0
- data/spec/active_record/user_spec.rb +37 -0
- data/spec/controllers/controller_activity_logging_spec.rb +124 -0
- data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
- data/spec/controllers/controller_http_basic_auth_spec.rb +68 -0
- data/spec/controllers/controller_oauth2_spec.rb +407 -0
- data/spec/controllers/controller_oauth_spec.rb +240 -0
- data/spec/controllers/controller_remember_me_spec.rb +117 -0
- data/spec/controllers/controller_session_timeout_spec.rb +80 -0
- data/spec/controllers/controller_spec.rb +215 -0
- data/spec/orm/active_record.rb +21 -0
- data/spec/rails_app/app/active_record/authentication.rb +3 -0
- data/spec/rails_app/app/active_record/user.rb +5 -0
- data/spec/rails_app/app/active_record/user_provider.rb +3 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +265 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/sorcery_mailer.rb +32 -0
- data/spec/rails_app/app/views/application/index.html.erb +17 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_email.text.erb +9 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_needed_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
- data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
- data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
- data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
- data/spec/rails_app/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +56 -0
- data/spec/rails_app/config/boot.rb +4 -0
- data/spec/rails_app/config/database.yml +22 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +12 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +48 -0
- data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
- data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +19 -0
- data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +13 -0
- data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +16 -0
- data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +22 -0
- data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
- data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
- data/spec/rails_app/db/schema.rb +23 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/shared_examples/user_activation_shared_examples.rb +242 -0
- data/spec/shared_examples/user_activity_logging_shared_examples.rb +97 -0
- data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +156 -0
- data/spec/shared_examples/user_oauth_shared_examples.rb +36 -0
- data/spec/shared_examples/user_remember_me_shared_examples.rb +57 -0
- data/spec/shared_examples/user_reset_password_shared_examples.rb +263 -0
- data/spec/shared_examples/user_shared_examples.rb +467 -0
- data/spec/sorcery_crypto_providers_spec.rb +198 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +41 -0
- metadata +350 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Model
|
|
3
|
+
module Submodules
|
|
4
|
+
# This submodule helps you login users from external providers such as Twitter.
|
|
5
|
+
# This is the model part which handles finding the user using access tokens.
|
|
6
|
+
# For the controller options see Sorcery::Controller::External.
|
|
7
|
+
#
|
|
8
|
+
# Socery assumes (read: requires) you will create external users in the same table where
|
|
9
|
+
# you keep your regular users,
|
|
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
|
|
12
|
+
# for example (a one-to-many relationship).
|
|
13
|
+
#
|
|
14
|
+
# External users will have a null crypted_password field, since we do not hold their password.
|
|
15
|
+
# They will not be sent activation emails on creation.
|
|
16
|
+
module External
|
|
17
|
+
def self.included(base)
|
|
18
|
+
base.sorcery_config.class_eval do
|
|
19
|
+
attr_accessor :authentications_class,
|
|
20
|
+
:authentications_user_id_attribute_name,
|
|
21
|
+
:provider_attribute_name,
|
|
22
|
+
:provider_uid_attribute_name
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
base.sorcery_config.instance_eval do
|
|
27
|
+
@defaults.merge!(:@authentications_class => nil,
|
|
28
|
+
:@authentications_user_id_attribute_name => :user_id,
|
|
29
|
+
:@provider_attribute_name => :provider,
|
|
30
|
+
:@provider_uid_attribute_name => :uid)
|
|
31
|
+
|
|
32
|
+
reset!
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
base.send(:include, InstanceMethods)
|
|
36
|
+
base.extend(ClassMethods)
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module ClassMethods
|
|
41
|
+
# takes a provider and uid and finds a user by them.
|
|
42
|
+
def load_from_provider(provider,uid)
|
|
43
|
+
config = sorcery_config
|
|
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
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
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
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Model
|
|
3
|
+
module Submodules
|
|
4
|
+
# The Remember Me submodule takes care of setting the user's cookie so that he will
|
|
5
|
+
# be automatically logged in to the site on every visit,
|
|
6
|
+
# until the cookie expires.
|
|
7
|
+
module RememberMe
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.sorcery_config.class_eval do
|
|
10
|
+
attr_accessor :remember_me_token_attribute_name, # the attribute in the model class.
|
|
11
|
+
:remember_me_token_expires_at_attribute_name, # the expires attribute in the model class.
|
|
12
|
+
:remember_me_for # how long in seconds to remember.
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
base.sorcery_config.instance_eval do
|
|
17
|
+
@defaults.merge!(:@remember_me_token_attribute_name => :remember_me_token,
|
|
18
|
+
:@remember_me_token_expires_at_attribute_name => :remember_me_token_expires_at,
|
|
19
|
+
:@remember_me_for => 7 * 60 * 60 * 24)
|
|
20
|
+
|
|
21
|
+
reset!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
base.send(:include, InstanceMethods)
|
|
25
|
+
base.sorcery_config.after_config << :define_remember_me_fields
|
|
26
|
+
|
|
27
|
+
base.extend(ClassMethods)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module ClassMethods
|
|
31
|
+
protected
|
|
32
|
+
|
|
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
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module InstanceMethods
|
|
41
|
+
# You shouldn't really use this one yourself - it's called by the controller's 'remember_me!' method.
|
|
42
|
+
def remember_me!
|
|
43
|
+
config = sorcery_config
|
|
44
|
+
self.sorcery_adapter.update_attributes(config.remember_me_token_attribute_name => TemporaryToken.generate_random_token,
|
|
45
|
+
config.remember_me_token_expires_at_attribute_name => Time.now.in_time_zone + config.remember_me_for)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def has_remember_me_token?
|
|
49
|
+
self.send(sorcery_config.remember_me_token_attribute_name).present?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# You shouldn't really use this one yourself - it's called by the controller's 'forget_me!' method.
|
|
53
|
+
def forget_me!
|
|
54
|
+
config = sorcery_config
|
|
55
|
+
self.sorcery_adapter.update_attributes(config.remember_me_token_attribute_name => nil,
|
|
56
|
+
config.remember_me_token_expires_at_attribute_name => nil)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Model
|
|
3
|
+
module Submodules
|
|
4
|
+
# This submodule adds the ability to reset password via email confirmation.
|
|
5
|
+
# When the user requests an email is sent to him with a url.
|
|
6
|
+
# The url includes a token, which is also saved with the user's record in the db.
|
|
7
|
+
# The token has configurable expiration.
|
|
8
|
+
# When the user clicks the url in the email, providing the token has not yet expired,
|
|
9
|
+
# he will be able to reset his password via a form.
|
|
10
|
+
#
|
|
11
|
+
# When using this submodule, supplying a mailer is mandatory.
|
|
12
|
+
module ResetPassword
|
|
13
|
+
def self.included(base)
|
|
14
|
+
base.sorcery_config.class_eval do
|
|
15
|
+
attr_accessor :reset_password_token_attribute_name, # reset password code attribute name.
|
|
16
|
+
:reset_password_token_expires_at_attribute_name, # expires at attribute name.
|
|
17
|
+
:reset_password_email_sent_at_attribute_name, # when was email sent, used for hammering
|
|
18
|
+
# protection.
|
|
19
|
+
|
|
20
|
+
:reset_password_mailer, # mailer class. Needed.
|
|
21
|
+
|
|
22
|
+
:reset_password_mailer_disabled, # when true sorcery will not automatically
|
|
23
|
+
# email password reset details and allow you to
|
|
24
|
+
# manually handle how and when email is sent
|
|
25
|
+
|
|
26
|
+
:reset_password_email_method_name, # reset password email method on your
|
|
27
|
+
# mailer class.
|
|
28
|
+
|
|
29
|
+
:reset_password_expiration_period, # how many seconds before the reset request
|
|
30
|
+
# expires. nil for never expires.
|
|
31
|
+
|
|
32
|
+
:reset_password_time_between_emails # hammering protection, how long to wait
|
|
33
|
+
# before allowing another email to be sent.
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
base.sorcery_config.instance_eval do
|
|
38
|
+
@defaults.merge!(:@reset_password_token_attribute_name => :reset_password_token,
|
|
39
|
+
:@reset_password_token_expires_at_attribute_name => :reset_password_token_expires_at,
|
|
40
|
+
:@reset_password_email_sent_at_attribute_name => :reset_password_email_sent_at,
|
|
41
|
+
:@reset_password_mailer => nil,
|
|
42
|
+
:@reset_password_mailer_disabled => false,
|
|
43
|
+
:@reset_password_email_method_name => :reset_password_email,
|
|
44
|
+
:@reset_password_expiration_period => nil,
|
|
45
|
+
:@reset_password_time_between_emails => 5 * 60 )
|
|
46
|
+
|
|
47
|
+
reset!
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
base.extend(ClassMethods)
|
|
51
|
+
|
|
52
|
+
base.sorcery_config.after_config << :validate_mailer_defined
|
|
53
|
+
base.sorcery_config.after_config << :define_reset_password_fields
|
|
54
|
+
|
|
55
|
+
base.send(:include, InstanceMethods)
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module ClassMethods
|
|
60
|
+
# Find user by token, also checks for expiration.
|
|
61
|
+
# Returns the user if token found and is valid.
|
|
62
|
+
def load_from_reset_password_token(token)
|
|
63
|
+
token_attr_name = @sorcery_config.reset_password_token_attribute_name
|
|
64
|
+
token_expiration_date_attr = @sorcery_config.reset_password_token_expires_at_attribute_name
|
|
65
|
+
load_from_token(token, token_attr_name, token_expiration_date_attr)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
protected
|
|
69
|
+
|
|
70
|
+
# This submodule requires the developer to define his own mailer class to be used by it
|
|
71
|
+
# when reset_password_mailer_disabled is false
|
|
72
|
+
def validate_mailer_defined
|
|
73
|
+
msg = "To use reset_password submodule, you must define a mailer (config.reset_password_mailer = YourMailerClass)."
|
|
74
|
+
raise ArgumentError, msg if @sorcery_config.reset_password_mailer == nil and @sorcery_config.reset_password_mailer_disabled == false
|
|
75
|
+
end
|
|
76
|
+
|
|
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
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
module InstanceMethods
|
|
86
|
+
# generates a reset code with expiration
|
|
87
|
+
def generate_reset_password_token!
|
|
88
|
+
config = sorcery_config
|
|
89
|
+
attributes = {config.reset_password_token_attribute_name => TemporaryToken.generate_random_token,
|
|
90
|
+
config.reset_password_email_sent_at_attribute_name => Time.now.in_time_zone}
|
|
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
|
|
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
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Clears token and tries to update the new password for the user.
|
|
108
|
+
def change_password!(new_password)
|
|
109
|
+
clear_reset_password_token
|
|
110
|
+
self.send(:"#{sorcery_config.password_attribute_name}=", new_password)
|
|
111
|
+
sorcery_adapter.save
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
protected
|
|
115
|
+
|
|
116
|
+
def send_reset_password_email!
|
|
117
|
+
generic_send_email(:reset_password_email_method_name, :reset_password_mailer)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Clears the token.
|
|
121
|
+
def clear_reset_password_token
|
|
122
|
+
config = sorcery_config
|
|
123
|
+
self.send(:"#{config.reset_password_token_attribute_name}=", nil)
|
|
124
|
+
self.send(:"#{config.reset_password_token_expires_at_attribute_name}=", nil) if config.reset_password_expiration_period
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module Sorcery
|
|
2
|
+
module Model
|
|
3
|
+
module Submodules
|
|
4
|
+
# This submodule adds the ability to make the user activate his account via email
|
|
5
|
+
# or any other way in which he can recieve an activation code.
|
|
6
|
+
# with the activation code the user may activate his account.
|
|
7
|
+
# When using this submodule, supplying a mailer is mandatory.
|
|
8
|
+
module UserActivation
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.sorcery_config.class_eval do
|
|
11
|
+
attr_accessor :activation_state_attribute_name, # the attribute name to hold activation state
|
|
12
|
+
# (active/pending).
|
|
13
|
+
|
|
14
|
+
:activation_token_attribute_name, # the attribute name to hold activation code
|
|
15
|
+
# (sent by email).
|
|
16
|
+
|
|
17
|
+
:activation_token_expires_at_attribute_name, # the attribute name to hold activation code
|
|
18
|
+
# expiration date.
|
|
19
|
+
|
|
20
|
+
:activation_token_expiration_period, # how many seconds before the activation code
|
|
21
|
+
# expires. nil for never expires.
|
|
22
|
+
|
|
23
|
+
:user_activation_mailer, # your mailer class. Required when
|
|
24
|
+
# activation_mailer_disabled == false.
|
|
25
|
+
|
|
26
|
+
:activation_mailer_disabled, # when true sorcery will not automatically
|
|
27
|
+
# email activation details and allow you to
|
|
28
|
+
# manually handle how and when email is sent
|
|
29
|
+
|
|
30
|
+
:activation_needed_email_method_name, # activation needed email method on your
|
|
31
|
+
# mailer class.
|
|
32
|
+
|
|
33
|
+
:activation_success_email_method_name, # activation success email method on your
|
|
34
|
+
# mailer class.
|
|
35
|
+
|
|
36
|
+
:prevent_non_active_users_to_login # do you want to prevent or allow users that
|
|
37
|
+
# did not activate by email to login?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
base.sorcery_config.instance_eval do
|
|
41
|
+
@defaults.merge!(:@activation_state_attribute_name => :activation_state,
|
|
42
|
+
:@activation_token_attribute_name => :activation_token,
|
|
43
|
+
:@activation_token_expires_at_attribute_name => :activation_token_expires_at,
|
|
44
|
+
:@activation_token_expiration_period => nil,
|
|
45
|
+
:@user_activation_mailer => nil,
|
|
46
|
+
:@activation_mailer_disabled => false,
|
|
47
|
+
:@activation_needed_email_method_name => :activation_needed_email,
|
|
48
|
+
:@activation_success_email_method_name => :activation_success_email,
|
|
49
|
+
:@prevent_non_active_users_to_login => true)
|
|
50
|
+
reset!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
base.class_eval do
|
|
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?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
base.sorcery_config.after_config << :validate_mailer_defined
|
|
61
|
+
base.sorcery_config.after_config << :define_user_activation_fields
|
|
62
|
+
base.sorcery_config.before_authenticate << :prevent_non_active_login
|
|
63
|
+
|
|
64
|
+
base.extend(ClassMethods)
|
|
65
|
+
base.send(:include, InstanceMethods)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module ClassMethods
|
|
71
|
+
# Find user by token, also checks for expiration.
|
|
72
|
+
# Returns the user if token found and is valid.
|
|
73
|
+
def load_from_activation_token(token)
|
|
74
|
+
token_attr_name = @sorcery_config.activation_token_attribute_name
|
|
75
|
+
token_expiration_date_attr = @sorcery_config.activation_token_expires_at_attribute_name
|
|
76
|
+
load_from_token(token, token_attr_name, token_expiration_date_attr)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
protected
|
|
80
|
+
|
|
81
|
+
# This submodule requires the developer to define his own mailer class to be used by it
|
|
82
|
+
# when activation_mailer_disabled is false
|
|
83
|
+
def validate_mailer_defined
|
|
84
|
+
msg = "To use user_activation submodule, you must define a mailer (config.user_activation_mailer = YourMailerClass)."
|
|
85
|
+
raise ArgumentError, msg if @sorcery_config.user_activation_mailer == nil and @sorcery_config.activation_mailer_disabled == false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def define_user_activation_fields
|
|
89
|
+
self.class_eval do
|
|
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
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
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
|
+
|
|
106
|
+
# clears activation code, sets the user as 'active' and optionaly sends a success email.
|
|
107
|
+
def activate!
|
|
108
|
+
config = sorcery_config
|
|
109
|
+
self.send(:"#{config.activation_token_attribute_name}=", nil)
|
|
110
|
+
self.send(:"#{config.activation_state_attribute_name}=", "active")
|
|
111
|
+
send_activation_success_email! if send_activation_success_email?
|
|
112
|
+
sorcery_adapter.save(:validate => false, :raise_on_failure => true)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
protected
|
|
116
|
+
|
|
117
|
+
# called automatically after user initial creation.
|
|
118
|
+
def send_activation_needed_email!
|
|
119
|
+
generic_send_email(:activation_needed_email_method_name, :user_activation_mailer)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def send_activation_success_email!
|
|
123
|
+
generic_send_email(:activation_success_email_method_name, :user_activation_mailer)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def send_activation_success_email?
|
|
127
|
+
!external? && (
|
|
128
|
+
!(sorcery_config.activation_success_email_method_name.nil? ||
|
|
129
|
+
sorcery_config.activation_mailer_disabled == true)
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def send_activation_needed_email?
|
|
134
|
+
!external? && (
|
|
135
|
+
!(sorcery_config.activation_needed_email_method_name.nil? ||
|
|
136
|
+
sorcery_config.activation_mailer_disabled == true)
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def prevent_non_active_login
|
|
141
|
+
config = sorcery_config
|
|
142
|
+
config.prevent_non_active_users_to_login ? self.send(config.activation_state_attribute_name) == "active" : true
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Sorcery
|
|
4
|
+
module Model
|
|
5
|
+
# This module encapsulates the logic for temporary token.
|
|
6
|
+
# A temporary token is created to identify a user in scenarios
|
|
7
|
+
# such as reseting password and activating the user by email.
|
|
8
|
+
module TemporaryToken
|
|
9
|
+
def self.included(base)
|
|
10
|
+
base.extend(ClassMethods)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Random code, used for salt and temporary tokens.
|
|
14
|
+
def self.generate_random_token
|
|
15
|
+
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
def load_from_token(token, token_attr_name, token_expiration_date_attr)
|
|
20
|
+
return nil if token.blank?
|
|
21
|
+
user = sorcery_adapter.find_by_token(token_attr_name,token)
|
|
22
|
+
if !user.blank? && !user.send(token_expiration_date_attr).nil?
|
|
23
|
+
return Time.now.in_time_zone < user.send(token_expiration_date_attr) ? user : nil
|
|
24
|
+
end
|
|
25
|
+
user
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|