cbsorcery 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
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