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.
Files changed (135) hide show
  1. data/.document +5 -0
  2. data/.gitignore +56 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +40 -0
  5. data/CHANGELOG.md +263 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +360 -0
  9. data/Rakefile +6 -0
  10. data/gemfiles/active_record-rails40.gemfile +7 -0
  11. data/gemfiles/active_record-rails41.gemfile +7 -0
  12. data/lib/generators/sorcery/USAGE +22 -0
  13. data/lib/generators/sorcery/helpers.rb +40 -0
  14. data/lib/generators/sorcery/install_generator.rb +95 -0
  15. data/lib/generators/sorcery/templates/initializer.rb +451 -0
  16. data/lib/generators/sorcery/templates/migration/activity_logging.rb +10 -0
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +9 -0
  18. data/lib/generators/sorcery/templates/migration/core.rb +13 -0
  19. data/lib/generators/sorcery/templates/migration/external.rb +12 -0
  20. data/lib/generators/sorcery/templates/migration/remember_me.rb +8 -0
  21. data/lib/generators/sorcery/templates/migration/reset_password.rb +9 -0
  22. data/lib/generators/sorcery/templates/migration/user_activation.rb +9 -0
  23. data/lib/sorcery.rb +85 -0
  24. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  25. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  26. data/lib/sorcery/controller.rb +157 -0
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +82 -0
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +38 -0
  30. data/lib/sorcery/controller/submodules/external.rb +199 -0
  31. data/lib/sorcery/controller/submodules/http_basic_auth.rb +74 -0
  32. data/lib/sorcery/controller/submodules/remember_me.rb +81 -0
  33. data/lib/sorcery/controller/submodules/session_timeout.rb +56 -0
  34. data/lib/sorcery/crypto_providers/aes256.rb +51 -0
  35. data/lib/sorcery/crypto_providers/bcrypt.rb +97 -0
  36. data/lib/sorcery/crypto_providers/common.rb +35 -0
  37. data/lib/sorcery/crypto_providers/md5.rb +19 -0
  38. data/lib/sorcery/crypto_providers/sha1.rb +28 -0
  39. data/lib/sorcery/crypto_providers/sha256.rb +36 -0
  40. data/lib/sorcery/crypto_providers/sha512.rb +36 -0
  41. data/lib/sorcery/engine.rb +21 -0
  42. data/lib/sorcery/model.rb +183 -0
  43. data/lib/sorcery/model/config.rb +96 -0
  44. data/lib/sorcery/model/submodules/activity_logging.rb +70 -0
  45. data/lib/sorcery/model/submodules/brute_force_protection.rb +125 -0
  46. data/lib/sorcery/model/submodules/external.rb +100 -0
  47. data/lib/sorcery/model/submodules/remember_me.rb +62 -0
  48. data/lib/sorcery/model/submodules/reset_password.rb +131 -0
  49. data/lib/sorcery/model/submodules/user_activation.rb +149 -0
  50. data/lib/sorcery/model/temporary_token.rb +30 -0
  51. data/lib/sorcery/protocols/certs/ca-bundle.crt +5182 -0
  52. data/lib/sorcery/protocols/oauth.rb +42 -0
  53. data/lib/sorcery/protocols/oauth2.rb +47 -0
  54. data/lib/sorcery/providers/base.rb +27 -0
  55. data/lib/sorcery/providers/facebook.rb +63 -0
  56. data/lib/sorcery/providers/github.rb +51 -0
  57. data/lib/sorcery/providers/google.rb +51 -0
  58. data/lib/sorcery/providers/jira.rb +77 -0
  59. data/lib/sorcery/providers/linkedin.rb +66 -0
  60. data/lib/sorcery/providers/liveid.rb +53 -0
  61. data/lib/sorcery/providers/twitter.rb +59 -0
  62. data/lib/sorcery/providers/vk.rb +63 -0
  63. data/lib/sorcery/providers/xing.rb +64 -0
  64. data/lib/sorcery/railties/tasks.rake +6 -0
  65. data/lib/sorcery/test_helpers/internal.rb +78 -0
  66. data/lib/sorcery/test_helpers/internal/rails.rb +68 -0
  67. data/lib/sorcery/test_helpers/rails/controller.rb +21 -0
  68. data/lib/sorcery/test_helpers/rails/integration.rb +26 -0
  69. data/lib/sorcery/version.rb +3 -0
  70. data/sorcery.gemspec +34 -0
  71. data/spec/active_record/user_activation_spec.rb +18 -0
  72. data/spec/active_record/user_activity_logging_spec.rb +17 -0
  73. data/spec/active_record/user_brute_force_protection_spec.rb +16 -0
  74. data/spec/active_record/user_oauth_spec.rb +16 -0
  75. data/spec/active_record/user_remember_me_spec.rb +16 -0
  76. data/spec/active_record/user_reset_password_spec.rb +16 -0
  77. data/spec/active_record/user_spec.rb +37 -0
  78. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  79. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  80. data/spec/controllers/controller_http_basic_auth_spec.rb +68 -0
  81. data/spec/controllers/controller_oauth2_spec.rb +407 -0
  82. data/spec/controllers/controller_oauth_spec.rb +240 -0
  83. data/spec/controllers/controller_remember_me_spec.rb +117 -0
  84. data/spec/controllers/controller_session_timeout_spec.rb +80 -0
  85. data/spec/controllers/controller_spec.rb +215 -0
  86. data/spec/orm/active_record.rb +21 -0
  87. data/spec/rails_app/app/active_record/authentication.rb +3 -0
  88. data/spec/rails_app/app/active_record/user.rb +5 -0
  89. data/spec/rails_app/app/active_record/user_provider.rb +3 -0
  90. data/spec/rails_app/app/controllers/sorcery_controller.rb +265 -0
  91. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  92. data/spec/rails_app/app/mailers/sorcery_mailer.rb +32 -0
  93. data/spec/rails_app/app/views/application/index.html.erb +17 -0
  94. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  95. data/spec/rails_app/app/views/sorcery_mailer/activation_email.html.erb +17 -0
  96. data/spec/rails_app/app/views/sorcery_mailer/activation_email.text.erb +9 -0
  97. data/spec/rails_app/app/views/sorcery_mailer/activation_needed_email.html.erb +17 -0
  98. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
  99. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
  100. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
  101. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
  102. data/spec/rails_app/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  103. data/spec/rails_app/config.ru +4 -0
  104. data/spec/rails_app/config/application.rb +56 -0
  105. data/spec/rails_app/config/boot.rb +4 -0
  106. data/spec/rails_app/config/database.yml +22 -0
  107. data/spec/rails_app/config/environment.rb +5 -0
  108. data/spec/rails_app/config/environments/test.rb +37 -0
  109. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  110. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  111. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  112. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  113. data/spec/rails_app/config/initializers/session_store.rb +12 -0
  114. data/spec/rails_app/config/locales/en.yml +5 -0
  115. data/spec/rails_app/config/routes.rb +48 -0
  116. data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
  117. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +19 -0
  118. data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +13 -0
  119. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +16 -0
  120. data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +22 -0
  121. data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
  122. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
  123. data/spec/rails_app/db/schema.rb +23 -0
  124. data/spec/rails_app/db/seeds.rb +7 -0
  125. data/spec/shared_examples/user_activation_shared_examples.rb +242 -0
  126. data/spec/shared_examples/user_activity_logging_shared_examples.rb +97 -0
  127. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +156 -0
  128. data/spec/shared_examples/user_oauth_shared_examples.rb +36 -0
  129. data/spec/shared_examples/user_remember_me_shared_examples.rb +57 -0
  130. data/spec/shared_examples/user_reset_password_shared_examples.rb +263 -0
  131. data/spec/shared_examples/user_shared_examples.rb +467 -0
  132. data/spec/sorcery_crypto_providers_spec.rb +198 -0
  133. data/spec/spec.opts +2 -0
  134. data/spec/spec_helper.rb +41 -0
  135. 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