sorcery 0.7.0 → 0.8.0

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 (204) hide show
  1. data/.travis.yml +3 -0
  2. data/Gemfile +9 -6
  3. data/Gemfile.lock +106 -75
  4. data/README.rdoc +42 -55
  5. data/Rakefile +32 -7
  6. data/VERSION +1 -1
  7. data/lib/generators/sorcery/USAGE +22 -0
  8. data/lib/generators/sorcery/install_generator.rb +77 -0
  9. data/lib/generators/sorcery/templates/initializer.rb +417 -0
  10. data/lib/generators/sorcery/templates/migration/activity_logging.rb +17 -0
  11. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +13 -0
  12. data/lib/generators/{sorcery_migration/templates → sorcery/templates/migration}/core.rb +2 -2
  13. data/lib/generators/{sorcery_migration/templates → sorcery/templates/migration}/external.rb +1 -1
  14. data/lib/generators/sorcery/templates/migration/remember_me.rb +15 -0
  15. data/lib/generators/sorcery/templates/migration/reset_password.rb +17 -0
  16. data/lib/generators/sorcery/templates/migration/user_activation.rb +17 -0
  17. data/lib/sorcery/controller/submodules/activity_logging.rb +8 -11
  18. data/lib/sorcery/controller/submodules/brute_force_protection.rb +2 -3
  19. data/lib/sorcery/controller/submodules/external/protocols/oauth2.rb +25 -10
  20. data/lib/sorcery/controller/submodules/external/providers/facebook.rb +20 -5
  21. data/lib/sorcery/controller/submodules/external/providers/github.rb +15 -4
  22. data/lib/sorcery/controller/submodules/external/providers/google.rb +90 -0
  23. data/lib/sorcery/controller/submodules/external/providers/linkedin.rb +101 -0
  24. data/lib/sorcery/controller/submodules/external/providers/liveid.rb +91 -0
  25. data/lib/sorcery/controller/submodules/external/providers/twitter.rb +3 -2
  26. data/lib/sorcery/controller/submodules/external/providers/vkontakte.rb +94 -0
  27. data/lib/sorcery/controller/submodules/external.rb +89 -15
  28. data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -1
  29. data/lib/sorcery/controller/submodules/remember_me.rb +26 -15
  30. data/lib/sorcery/controller/submodules/session_timeout.rb +3 -3
  31. data/lib/sorcery/controller.rb +55 -46
  32. data/lib/sorcery/crypto_providers/bcrypt.rb +1 -0
  33. data/lib/sorcery/model/adapters/active_record.rb +21 -1
  34. data/lib/sorcery/model/adapters/mongo_mapper.rb +56 -0
  35. data/lib/sorcery/model/adapters/mongoid.rb +23 -4
  36. data/lib/sorcery/model/submodules/activity_logging.rb +4 -4
  37. data/lib/sorcery/model/submodules/brute_force_protection.rb +54 -15
  38. data/lib/sorcery/model/submodules/remember_me.rb +12 -7
  39. data/lib/sorcery/model/submodules/reset_password.rb +27 -11
  40. data/lib/sorcery/model/submodules/user_activation.rb +27 -8
  41. data/lib/sorcery/model/temporary_token.rb +4 -2
  42. data/lib/sorcery/model.rb +78 -47
  43. data/lib/sorcery/railties/tasks.rake +2 -0
  44. data/lib/sorcery.rb +10 -8
  45. data/sorcery.gemspec +107 -228
  46. data/spec/Gemfile +1 -1
  47. data/spec/Gemfile.lock +27 -23
  48. data/spec/README.md +12 -7
  49. data/spec/rails3/Gemfile +3 -3
  50. data/spec/rails3/Gemfile.lock +62 -55
  51. data/spec/rails3/app/controllers/application_controller.rb +85 -2
  52. data/spec/rails3/app/mailers/sorcery_mailer.rb +7 -0
  53. data/spec/rails3/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  54. data/spec/rails3/config/environments/in_memory.rb +35 -0
  55. data/spec/rails3/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +2 -0
  56. data/spec/rails3/spec/controller_activity_logging_spec.rb +16 -6
  57. data/spec/rails3/spec/controller_brute_force_protection_spec.rb +25 -3
  58. data/spec/rails3/spec/controller_oauth2_spec.rb +260 -20
  59. data/spec/rails3/spec/controller_oauth_spec.rb +111 -6
  60. data/spec/rails3/spec/controller_session_timeout_spec.rb +4 -4
  61. data/spec/rails3/spec/controller_spec.rb +37 -2
  62. data/spec/rails3/spec/integration_spec.rb +15 -15
  63. data/spec/rails3/spec/spec_helper.rb +0 -2
  64. data/spec/rails3_mongo_mapper/.gitignore +4 -0
  65. data/spec/rails3_mongo_mapper/.rspec +1 -0
  66. data/spec/{sinatra_modular → rails3_mongo_mapper}/Gemfile +6 -5
  67. data/spec/rails3_mongo_mapper/Gemfile.lock +156 -0
  68. data/spec/{sinatra → rails3_mongo_mapper}/Rakefile +3 -3
  69. data/spec/rails3_mongo_mapper/app/controllers/application_controller.rb +122 -0
  70. data/spec/rails3_mongo_mapper/app/helpers/application_helper.rb +2 -0
  71. data/spec/rails3_mongo_mapper/app/models/authentication.rb +6 -0
  72. data/spec/rails3_mongo_mapper/app/models/user.rb +5 -0
  73. data/spec/rails3_mongo_mapper/app/views/layouts/application.html.erb +14 -0
  74. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_email.html.erb +17 -0
  75. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_email.text.erb +9 -0
  76. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
  77. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
  78. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
  79. data/spec/rails3_mongo_mapper/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
  80. data/spec/rails3_mongo_mapper/config/application.rb +51 -0
  81. data/spec/rails3_mongo_mapper/config/boot.rb +13 -0
  82. data/spec/rails3_mongo_mapper/config/environment.rb +5 -0
  83. data/spec/rails3_mongo_mapper/config/environments/development.rb +30 -0
  84. data/spec/rails3_mongo_mapper/config/environments/in_memory.rb +0 -0
  85. data/spec/rails3_mongo_mapper/config/environments/production.rb +49 -0
  86. data/spec/rails3_mongo_mapper/config/environments/test.rb +35 -0
  87. data/spec/rails3_mongo_mapper/config/initializers/backtrace_silencers.rb +7 -0
  88. data/spec/rails3_mongo_mapper/config/initializers/inflections.rb +10 -0
  89. data/spec/rails3_mongo_mapper/config/initializers/mime_types.rb +5 -0
  90. data/spec/rails3_mongo_mapper/config/initializers/mongo.rb +2 -0
  91. data/spec/rails3_mongo_mapper/config/initializers/secret_token.rb +7 -0
  92. data/spec/rails3_mongo_mapper/config/initializers/session_store.rb +8 -0
  93. data/spec/rails3_mongo_mapper/config/locales/en.yml +5 -0
  94. data/spec/rails3_mongo_mapper/config/routes.rb +59 -0
  95. data/spec/rails3_mongo_mapper/config.ru +4 -0
  96. data/spec/rails3_mongo_mapper/db/schema.rb +23 -0
  97. data/spec/rails3_mongo_mapper/db/seeds.rb +7 -0
  98. data/spec/rails3_mongo_mapper/lib/tasks/.gitkeep +0 -0
  99. data/spec/rails3_mongo_mapper/public/404.html +26 -0
  100. data/spec/rails3_mongo_mapper/public/422.html +26 -0
  101. data/spec/rails3_mongo_mapper/public/500.html +26 -0
  102. data/spec/rails3_mongo_mapper/public/favicon.ico +0 -0
  103. data/spec/rails3_mongo_mapper/public/images/rails.png +0 -0
  104. data/spec/rails3_mongo_mapper/public/javascripts/application.js +2 -0
  105. data/spec/rails3_mongo_mapper/public/javascripts/controls.js +965 -0
  106. data/spec/rails3_mongo_mapper/public/javascripts/dragdrop.js +974 -0
  107. data/spec/rails3_mongo_mapper/public/javascripts/effects.js +1123 -0
  108. data/spec/rails3_mongo_mapper/public/javascripts/prototype.js +6001 -0
  109. data/spec/rails3_mongo_mapper/public/javascripts/rails.js +175 -0
  110. data/spec/rails3_mongo_mapper/public/robots.txt +5 -0
  111. data/spec/rails3_mongo_mapper/public/stylesheets/.gitkeep +0 -0
  112. data/spec/rails3_mongo_mapper/script/rails +6 -0
  113. data/spec/rails3_mongo_mapper/spec/controller_spec.rb +170 -0
  114. data/spec/rails3_mongo_mapper/spec/spec_helper.orig.rb +27 -0
  115. data/spec/rails3_mongo_mapper/spec/spec_helper.rb +55 -0
  116. data/spec/rails3_mongo_mapper/spec/user_activation_spec.rb +9 -0
  117. data/spec/rails3_mongo_mapper/spec/user_activity_logging_spec.rb +8 -0
  118. data/spec/rails3_mongo_mapper/spec/user_brute_force_protection_spec.rb +8 -0
  119. data/spec/rails3_mongo_mapper/spec/user_oauth_spec.rb +8 -0
  120. data/spec/rails3_mongo_mapper/spec/user_remember_me_spec.rb +8 -0
  121. data/spec/rails3_mongo_mapper/spec/user_reset_password_spec.rb +8 -0
  122. data/spec/rails3_mongo_mapper/spec/user_spec.rb +37 -0
  123. data/spec/rails3_mongo_mapper/vendor/plugins/.gitkeep +0 -0
  124. data/spec/rails3_mongoid/Gemfile +2 -1
  125. data/spec/rails3_mongoid/Gemfile.lock +47 -42
  126. data/spec/rails3_mongoid/app/controllers/application_controller.rb +19 -0
  127. data/spec/rails3_mongoid/config/mongoid.yml +1 -1
  128. data/spec/rails3_mongoid/script/rails +0 -0
  129. data/spec/rails3_mongoid/spec/controller_activity_logging_spec.rb +105 -0
  130. data/spec/rails3_mongoid/spec/controller_spec.rb +52 -1
  131. data/spec/rails3_mongoid/spec/user_spec.rb +1 -0
  132. data/spec/shared_examples/controller_oauth2_shared_examples.rb +20 -1
  133. data/spec/shared_examples/controller_oauth_shared_examples.rb +18 -0
  134. data/spec/shared_examples/user_activation_shared_examples.rb +72 -42
  135. data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
  136. data/spec/shared_examples/user_reset_password_shared_examples.rb +81 -28
  137. data/spec/shared_examples/user_shared_examples.rb +29 -1
  138. data/spec/sorcery_crypto_providers_spec.rb +14 -1
  139. metadata +111 -229
  140. data/lib/generators/sorcery_migration/sorcery_migration_generator.rb +0 -24
  141. data/lib/generators/sorcery_migration/templates/activity_logging.rb +0 -17
  142. data/lib/generators/sorcery_migration/templates/brute_force_protection.rb +0 -11
  143. data/lib/generators/sorcery_migration/templates/remember_me.rb +0 -15
  144. data/lib/generators/sorcery_migration/templates/reset_password.rb +0 -17
  145. data/lib/generators/sorcery_migration/templates/user_activation.rb +0 -17
  146. data/lib/sorcery/controller/adapters/sinatra.rb +0 -115
  147. data/lib/sorcery/initializers/initializer.rb +0 -199
  148. data/lib/sorcery/sinatra.rb +0 -4
  149. data/lib/sorcery/test_helpers/internal/sinatra.rb +0 -74
  150. data/lib/sorcery/test_helpers/internal/sinatra_modular.rb +0 -74
  151. data/lib/sorcery/test_helpers/sinatra.rb +0 -88
  152. data/spec/rails3/Rakefile.unused +0 -7
  153. data/spec/sinatra/Gemfile +0 -15
  154. data/spec/sinatra/Gemfile.lock +0 -115
  155. data/spec/sinatra/authentication.rb +0 -3
  156. data/spec/sinatra/db/migrate/activation/20101224223622_add_activation_to_users.rb +0 -17
  157. data/spec/sinatra/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +0 -17
  158. data/spec/sinatra/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +0 -11
  159. data/spec/sinatra/db/migrate/core/20101224223620_create_users.rb +0 -16
  160. data/spec/sinatra/db/migrate/external/20101224223628_create_authentications.rb +0 -14
  161. data/spec/sinatra/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +0 -15
  162. data/spec/sinatra/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +0 -13
  163. data/spec/sinatra/filters.rb +0 -27
  164. data/spec/sinatra/modular.rb +0 -157
  165. data/spec/sinatra/myapp.rb +0 -133
  166. data/spec/sinatra/spec/controller_activity_logging_spec.rb +0 -85
  167. data/spec/sinatra/spec/controller_brute_force_protection_spec.rb +0 -70
  168. data/spec/sinatra/spec/controller_http_basic_auth_spec.rb +0 -53
  169. data/spec/sinatra/spec/controller_oauth2_spec.rb +0 -96
  170. data/spec/sinatra/spec/controller_oauth_spec.rb +0 -100
  171. data/spec/sinatra/spec/controller_remember_me_spec.rb +0 -64
  172. data/spec/sinatra/spec/controller_session_timeout_spec.rb +0 -57
  173. data/spec/sinatra/spec/controller_spec.rb +0 -127
  174. data/spec/sinatra/spec/spec_helper.rb +0 -45
  175. data/spec/sinatra/user.rb +0 -6
  176. data/spec/sinatra/views/test_login.erb +0 -4
  177. data/spec/sinatra_modular/Gemfile.lock +0 -115
  178. data/spec/sinatra_modular/Rakefile +0 -11
  179. data/spec/sinatra_modular/authentication.rb +0 -3
  180. data/spec/sinatra_modular/db/migrate/activation/20101224223622_add_activation_to_users.rb +0 -17
  181. data/spec/sinatra_modular/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +0 -17
  182. data/spec/sinatra_modular/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +0 -11
  183. data/spec/sinatra_modular/db/migrate/core/20101224223620_create_users.rb +0 -16
  184. data/spec/sinatra_modular/db/migrate/external/20101224223628_create_authentications.rb +0 -14
  185. data/spec/sinatra_modular/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +0 -15
  186. data/spec/sinatra_modular/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +0 -13
  187. data/spec/sinatra_modular/filters.rb +0 -27
  188. data/spec/sinatra_modular/modular.rb +0 -157
  189. data/spec/sinatra_modular/myapp.rb +0 -133
  190. data/spec/sinatra_modular/sorcery_mailer.rb +0 -25
  191. data/spec/sinatra_modular/spec_modular/controller_activity_logging_spec.rb +0 -85
  192. data/spec/sinatra_modular/spec_modular/controller_brute_force_protection_spec.rb +0 -70
  193. data/spec/sinatra_modular/spec_modular/controller_http_basic_auth_spec.rb +0 -53
  194. data/spec/sinatra_modular/spec_modular/controller_oauth2_spec.rb +0 -96
  195. data/spec/sinatra_modular/spec_modular/controller_oauth_spec.rb +0 -100
  196. data/spec/sinatra_modular/spec_modular/controller_remember_me_spec.rb +0 -64
  197. data/spec/sinatra_modular/spec_modular/controller_session_timeout_spec.rb +0 -57
  198. data/spec/sinatra_modular/spec_modular/controller_spec.rb +0 -116
  199. data/spec/sinatra_modular/spec_modular/spec.opts +0 -2
  200. data/spec/sinatra_modular/spec_modular/spec_helper.rb +0 -51
  201. data/spec/sinatra_modular/user.rb +0 -6
  202. data/spec/sinatra_modular/views/test_login.erb +0 -4
  203. /data/spec/{sinatra → rails3_mongo_mapper/app/mailers}/sorcery_mailer.rb +0 -0
  204. /data/spec/{sinatra → rails3_mongo_mapper}/spec/spec.opts +0 -0
@@ -24,7 +24,10 @@ module Sorcery
24
24
  base.send(:include, InstanceMethods)
25
25
 
26
26
  base.sorcery_config.after_config << :define_remember_me_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
27
-
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
+
28
31
  base.extend(ClassMethods)
29
32
  end
30
33
 
@@ -36,23 +39,25 @@ module Sorcery
36
39
  field sorcery_config.remember_me_token_expires_at_attribute_name, :type => Time
37
40
  end
38
41
 
42
+ def define_remember_me_mongo_mapper_fields
43
+ key sorcery_config.remember_me_token_attribute_name, String
44
+ key sorcery_config.remember_me_token_expires_at_attribute_name, Time
45
+ end
39
46
  end
40
47
 
41
48
  module InstanceMethods
42
49
  # You shouldn't really use this one yourself - it's called by the controller's 'remember_me!' method.
43
50
  def remember_me!
44
51
  config = sorcery_config
45
- self.send(:"#{config.remember_me_token_attribute_name}=", TemporaryToken.generate_random_token)
46
- self.send(:"#{config.remember_me_token_expires_at_attribute_name}=", Time.now + config.remember_me_for)
47
- self.save!(:validate => false)
52
+ self.update_many_attributes(config.remember_me_token_attribute_name => TemporaryToken.generate_random_token,
53
+ config.remember_me_token_expires_at_attribute_name => Time.now.in_time_zone + config.remember_me_for)
48
54
  end
49
55
 
50
56
  # You shouldn't really use this one yourself - it's called by the controller's 'forget_me!' method.
51
57
  def forget_me!
52
58
  config = sorcery_config
53
- self.send(:"#{config.remember_me_token_attribute_name}=", nil)
54
- self.send(:"#{config.remember_me_token_expires_at_attribute_name}=", nil)
55
- self.save!(:validate => false)
59
+ self.update_many_attributes(config.remember_me_token_attribute_name => nil,
60
+ config.remember_me_token_expires_at_attribute_name => nil)
56
61
  end
57
62
  end
58
63
  end
@@ -18,6 +18,11 @@ module Sorcery
18
18
  # protection.
19
19
 
20
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
+
21
26
  :reset_password_email_method_name, # reset password email method on your
22
27
  # mailer class.
23
28
 
@@ -34,6 +39,7 @@ module Sorcery
34
39
  :@reset_password_token_expires_at_attribute_name => :reset_password_token_expires_at,
35
40
  :@reset_password_email_sent_at_attribute_name => :reset_password_email_sent_at,
36
41
  :@reset_password_mailer => nil,
42
+ :@reset_password_mailer_disabled => false,
37
43
  :@reset_password_email_method_name => :reset_password_email,
38
44
  :@reset_password_expiration_period => nil,
39
45
  :@reset_password_time_between_emails => 5 * 60 )
@@ -45,6 +51,9 @@ module Sorcery
45
51
 
46
52
  base.sorcery_config.after_config << :validate_mailer_defined
47
53
  base.sorcery_config.after_config << :define_reset_password_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
54
+ if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
55
+ base.sorcery_config.after_config << :define_reset_password_mongo_mapper_fields
56
+ end
48
57
 
49
58
  base.send(:include, InstanceMethods)
50
59
 
@@ -61,16 +70,23 @@ module Sorcery
61
70
 
62
71
  protected
63
72
 
64
- # This submodule requires the developer to define his own mailer class to be used by it.
73
+ # This submodule requires the developer to define his own mailer class to be used by it
74
+ # when reset_password_mailer_disabled is false
65
75
  def validate_mailer_defined
66
76
  msg = "To use reset_password submodule, you must define a mailer (config.reset_password_mailer = YourMailerClass)."
67
- raise ArgumentError, msg if @sorcery_config.reset_password_mailer == nil
77
+ raise ArgumentError, msg if @sorcery_config.reset_password_mailer == nil and @sorcery_config.reset_password_mailer_disabled == false
68
78
  end
69
79
 
70
80
  def define_reset_password_mongoid_fields
71
81
  field sorcery_config.reset_password_token_attribute_name, :type => String
72
- field sorcery_config.reset_password_token_expires_at_attribute_name, :type => DateTime
73
- field sorcery_config.reset_password_email_sent_at_attribute_name, :type => DateTime
82
+ field sorcery_config.reset_password_token_expires_at_attribute_name, :type => Time
83
+ field sorcery_config.reset_password_email_sent_at_attribute_name, :type => Time
84
+ end
85
+
86
+ def define_reset_password_mongo_mapper_fields
87
+ key sorcery_config.reset_password_token_attribute_name, String
88
+ key sorcery_config.reset_password_token_expires_at_attribute_name, Time
89
+ key sorcery_config.reset_password_email_sent_at_attribute_name, Time
74
90
  end
75
91
  end
76
92
 
@@ -79,13 +95,13 @@ module Sorcery
79
95
  def deliver_reset_password_instructions!
80
96
  config = sorcery_config
81
97
  # hammering protection
82
- return 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
83
- self.send(:"#{config.reset_password_token_attribute_name}=", TemporaryToken.generate_random_token)
84
- self.send(:"#{config.reset_password_token_expires_at_attribute_name}=", Time.now.utc + config.reset_password_expiration_period) if config.reset_password_expiration_period
85
- self.send(:"#{config.reset_password_email_sent_at_attribute_name}=", Time.now.utc)
98
+ 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
99
+ attributes = {config.reset_password_token_attribute_name => TemporaryToken.generate_random_token,
100
+ config.reset_password_email_sent_at_attribute_name => Time.now.in_time_zone}
101
+ 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
86
102
  self.class.transaction do
87
- self.save!(:validate => false)
88
- generic_send_email(:reset_password_email_method_name, :reset_password_mailer)
103
+ self.update_many_attributes(attributes)
104
+ generic_send_email(:reset_password_email_method_name, :reset_password_mailer) unless config.reset_password_mailer_disabled
89
105
  end
90
106
  end
91
107
 
@@ -109,4 +125,4 @@ module Sorcery
109
125
  end
110
126
  end
111
127
  end
112
- end
128
+ end
@@ -20,7 +20,13 @@ module Sorcery
20
20
  :activation_token_expiration_period, # how many seconds before the activation code
21
21
  # expires. nil for never expires.
22
22
 
23
- :user_activation_mailer, # your mailer class. Required.
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
+
24
30
  :activation_needed_email_method_name, # activation needed email method on your
25
31
  # mailer class.
26
32
 
@@ -37,6 +43,7 @@ module Sorcery
37
43
  :@activation_token_expires_at_attribute_name => :activation_token_expires_at,
38
44
  :@activation_token_expiration_period => nil,
39
45
  :@user_activation_mailer => nil,
46
+ :@activation_mailer_disabled => false,
40
47
  :@activation_needed_email_method_name => :activation_needed_email,
41
48
  :@activation_success_email_method_name => :activation_success_email,
42
49
  :@prevent_non_active_users_to_login => true)
@@ -47,11 +54,14 @@ module Sorcery
47
54
  # don't setup activation if no password supplied - this user is created automatically
48
55
  before_create :setup_activation, :if => Proc.new { |user| user.send(sorcery_config.password_attribute_name).present? }
49
56
  # don't send activation needed email if no crypted password created - this user is external (OAuth etc.)
50
- after_create :send_activation_needed_email!, :if => Proc.new { |user| !user.external?}
57
+ after_create :send_activation_needed_email!, :if => Proc.new { |user| !user.external? }
51
58
  end
52
59
 
53
60
  base.sorcery_config.after_config << :validate_mailer_defined
54
61
  base.sorcery_config.after_config << :define_user_activation_mongoid_fields if defined?(Mongoid) and base.ancestors.include?(Mongoid::Document)
62
+ if defined?(MongoMapper) and base.ancestors.include?(MongoMapper::Document)
63
+ base.sorcery_config.after_config << :define_user_activation_mongo_mapper_fields
64
+ end
55
65
  base.sorcery_config.before_authenticate << :prevent_non_active_login
56
66
 
57
67
  base.extend(ClassMethods)
@@ -71,17 +81,26 @@ module Sorcery
71
81
 
72
82
  protected
73
83
 
74
- # This submodule requires the developer to define his own mailer class to be used by it.
84
+ # This submodule requires the developer to define his own mailer class to be used by it
85
+ # when activation_mailer_disabled is false
75
86
  def validate_mailer_defined
76
87
  msg = "To use user_activation submodule, you must define a mailer (config.user_activation_mailer = YourMailerClass)."
77
- raise ArgumentError, msg if @sorcery_config.user_activation_mailer == nil
88
+ raise ArgumentError, msg if @sorcery_config.user_activation_mailer == nil and @sorcery_config.activation_mailer_disabled == false
78
89
  end
79
90
 
80
91
  def define_user_activation_mongoid_fields
81
92
  self.class_eval do
82
93
  field sorcery_config.activation_state_attribute_name, :type => String
83
94
  field sorcery_config.activation_token_attribute_name, :type => String
84
- field sorcery_config.activation_token_expires_at_attribute_name, :type => DateTime
95
+ field sorcery_config.activation_token_expires_at_attribute_name, :type => Time
96
+ end
97
+ end
98
+
99
+ def define_user_activation_mongo_mapper_fields
100
+ self.class_eval do
101
+ key sorcery_config.activation_state_attribute_name, String
102
+ key sorcery_config.activation_token_attribute_name, String
103
+ key sorcery_config.activation_token_expires_at_attribute_name, Time
85
104
  end
86
105
  end
87
106
  end
@@ -103,16 +122,16 @@ module Sorcery
103
122
  generated_activation_token = TemporaryToken.generate_random_token
104
123
  self.send(:"#{config.activation_token_attribute_name}=", generated_activation_token)
105
124
  self.send(:"#{config.activation_state_attribute_name}=", "pending")
106
- self.send(:"#{config.activation_token_expires_at_attribute_name}=", Time.now.utc + config.activation_token_expiration_period) if config.activation_token_expiration_period
125
+ 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
107
126
  end
108
127
 
109
128
  # called automatically after user initial creation.
110
129
  def send_activation_needed_email!
111
- generic_send_email(:activation_needed_email_method_name, :user_activation_mailer) unless sorcery_config.activation_needed_email_method_name.nil?
130
+ generic_send_email(:activation_needed_email_method_name, :user_activation_mailer) unless sorcery_config.activation_needed_email_method_name.nil? or sorcery_config.activation_mailer_disabled == true
112
131
  end
113
132
 
114
133
  def send_activation_success_email!
115
- generic_send_email(:activation_success_email_method_name, :user_activation_mailer) unless sorcery_config.activation_success_email_method_name.nil?
134
+ generic_send_email(:activation_success_email_method_name, :user_activation_mailer) unless sorcery_config.activation_success_email_method_name.nil? or sorcery_config.activation_mailer_disabled == true
116
135
  end
117
136
 
118
137
  def prevent_non_active_login
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Sorcery
2
4
  module Model
3
5
  # This module encapsulates the logic for temporary token.
@@ -10,7 +12,7 @@ module Sorcery
10
12
 
11
13
  # Random code, used for salt and temporary tokens.
12
14
  def self.generate_random_token
13
- Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
15
+ SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
14
16
  end
15
17
 
16
18
  module ClassMethods
@@ -18,7 +20,7 @@ module Sorcery
18
20
  return nil if token.blank?
19
21
  user = find_by_sorcery_token(token_attr_name,token)
20
22
  if !user.blank? && !user.send(token_expiration_date_attr).nil?
21
- return Time.now.utc < user.send(token_expiration_date_attr) ? user : nil
23
+ return Time.now.in_time_zone < user.send(token_expiration_date_attr) ? user : nil
22
24
  end
23
25
  user
24
26
  end
data/lib/sorcery/model.rb CHANGED
@@ -3,8 +3,8 @@ module Sorcery
3
3
  # It should be included into the ORM base class.
4
4
  # In the case of Rails this is usually ActiveRecord (actually, in that case, the plugin does this automatically).
5
5
  #
6
- # When included it defines a single method: 'activate_sorcery!' which when called adds the other capabilities
7
- # to the class.
6
+ # When included it defines a single method: 'authenticates_with_sorcery!'
7
+ # which when called adds the other capabilities to the class.
8
8
  # This method is also the place to configure the plugin in the Model layer.
9
9
  module Model
10
10
  def self.included(klass)
@@ -17,38 +17,39 @@ module Sorcery
17
17
  include InstanceMethods
18
18
  include TemporaryToken
19
19
  end
20
-
20
+
21
21
  include_required_submodules!
22
22
 
23
23
  # This runs the options block set in the initializer on the model class.
24
24
  ::Sorcery::Controller::Config.user_config.tap{|blk| blk.call(@sorcery_config) if blk}
25
-
25
+
26
26
  init_mongoid_support! if defined?(Mongoid) and self.ancestors.include?(Mongoid::Document)
27
+ init_mongo_mapper_support! if defined?(MongoMapper) and self.ancestors.include?(MongoMapper::Document)
27
28
 
28
29
  init_orm_hooks!
29
-
30
+
30
31
  @sorcery_config.after_config << :add_config_inheritance if @sorcery_config.subclasses_inherit_config
31
32
  @sorcery_config.after_config.each { |c| send(c) }
32
33
  end
33
-
34
+
34
35
  protected
35
-
36
- # includes required submodules into the model class,
36
+
37
+ # includes required submodules into the model class,
37
38
  # which usually is called User.
38
39
  def include_required_submodules!
39
40
  self.class_eval do
40
41
  @sorcery_config.submodules = ::Sorcery::Controller::Config.submodules
41
42
  @sorcery_config.submodules.each do |mod|
42
43
  begin
43
- include Submodules.const_get(mod.to_s.split("_").map {|p| p.capitalize}.join(""))
44
+ include Submodules.const_get(mod.to_s.split("_").map {|p| p.capitalize}.join(""))
44
45
  rescue NameError
45
- # don't stop on a missing submodule. Needed because some submodules are only defined
46
+ # don't stop on a missing submodule. Needed because some submodules are only defined
46
47
  # in the controller side.
47
48
  end
48
49
  end
49
50
  end
50
51
  end
51
-
52
+
52
53
  # defines mongoid fields on the model class,
53
54
  # using 1.8.x hash syntax to perserve compatibility.
54
55
  def init_mongoid_support!
@@ -61,55 +62,80 @@ module Sorcery
61
62
  field sorcery_config.salt_attribute_name, :type => String
62
63
  end
63
64
  end
64
-
65
+
66
+ # defines mongo_mapper fields on the model class,
67
+ def init_mongo_mapper_support!
68
+ self.class_eval do
69
+ sorcery_config.username_attribute_names.each do |username|
70
+ key username, String
71
+ end
72
+ key sorcery_config.email_attribute_name, String unless sorcery_config.username_attribute_names.include?(sorcery_config.email_attribute_name)
73
+ key sorcery_config.crypted_password_attribute_name, String
74
+ key sorcery_config.salt_attribute_name, String
75
+ end
76
+ end
77
+
65
78
  # add virtual password accessor and ORM callbacks.
66
79
  def init_orm_hooks!
67
80
  self.class_eval do
68
81
  attr_accessor @sorcery_config.password_attribute_name
69
82
  attr_protected @sorcery_config.crypted_password_attribute_name, @sorcery_config.salt_attribute_name
70
- before_save :encrypt_password, :if => Proc.new { |record| record.send(sorcery_config.password_attribute_name).present? }
71
- after_save :clear_virtual_password, :if => Proc.new { |record| record.send(sorcery_config.password_attribute_name).present? }
83
+ before_save :encrypt_password, :if => Proc.new { |record|
84
+ record.send(sorcery_config.password_attribute_name).present?
85
+ }
86
+ after_save :clear_virtual_password, :if => Proc.new { |record|
87
+ record.send(sorcery_config.password_attribute_name).present?
88
+ }
72
89
  end
73
90
  end
74
91
  end
75
92
  end
76
93
  end
77
-
94
+
78
95
  module ClassMethods
79
96
  # Returns the class instance variable for configuration, when called by the class itself.
80
97
  def sorcery_config
81
98
  @sorcery_config
82
99
  end
83
-
100
+
84
101
  # The default authentication method.
85
102
  # Takes a username and password,
86
103
  # Finds the user by the username and compares the user's password to the one supplied to the method.
87
104
  # returns the user if success, nil otherwise.
88
105
  def authenticate(*credentials)
89
106
  raise ArgumentError, "at least 2 arguments required" if credentials.size < 2
107
+ credentials[0].downcase! if @sorcery_config.downcase_username_before_authenticating
90
108
  user = find_by_credentials(credentials)
109
+
110
+ set_encryption_attributes()
111
+
91
112
  _salt = user.send(@sorcery_config.salt_attribute_name) if user && !@sorcery_config.salt_attribute_name.nil? && !@sorcery_config.encryption_provider.nil?
92
113
  user if user && @sorcery_config.before_authenticate.all? {|c| user.send(c)} && credentials_match?(user.send(@sorcery_config.crypted_password_attribute_name),credentials[1],_salt)
93
114
  end
94
-
115
+
95
116
  # encrypt tokens using current encryption_provider.
96
117
  def encrypt(*tokens)
97
118
  return tokens.first if @sorcery_config.encryption_provider.nil?
98
-
99
- @sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches
100
- @sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token
119
+
120
+ set_encryption_attributes()
121
+
101
122
  CryptoProviders::AES256.key = @sorcery_config.encryption_key
102
123
  @sorcery_config.encryption_provider.encrypt(*tokens)
103
124
  end
104
-
125
+
105
126
  protected
106
-
127
+
128
+ def set_encryption_attributes()
129
+ @sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches
130
+ @sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token
131
+ end
132
+
107
133
  # Calls the configured encryption provider to compare the supplied password with the encrypted one.
108
134
  def credentials_match?(crypted, *tokens)
109
135
  return crypted == tokens.join if @sorcery_config.encryption_provider.nil?
110
136
  @sorcery_config.encryption_provider.matches?(crypted, *tokens)
111
137
  end
112
-
138
+
113
139
  def add_config_inheritance
114
140
  self.class_eval do
115
141
  def self.inherited(subclass)
@@ -123,23 +149,23 @@ module Sorcery
123
149
  end
124
150
  end
125
151
  end
126
-
152
+
127
153
  end
128
-
154
+
129
155
  module InstanceMethods
130
156
  # Returns the class instance variable for configuration, when called by an instance.
131
157
  def sorcery_config
132
158
  self.class.sorcery_config
133
159
  end
134
-
160
+
135
161
  # identifies whether this user is regular, i.e. we hold his credentials in our db,
136
162
  # or that he is external, and his credentials are saved elsewhere (twitter/facebook etc.).
137
163
  def external?
138
164
  send(sorcery_config.crypted_password_attribute_name).nil?
139
165
  end
140
-
166
+
141
167
  protected
142
-
168
+
143
169
  # creates new salt and saves it.
144
170
  # encrypts password with salt and saves it.
145
171
  def encrypt_password
@@ -152,47 +178,50 @@ module Sorcery
152
178
  config = sorcery_config
153
179
  self.send(:"#{config.password_attribute_name}=", nil)
154
180
  end
155
-
181
+
156
182
  # calls the requested email method on the configured mailer
157
183
  # supports both the ActionMailer 3 way of calling, and the plain old Ruby object way.
158
184
  def generic_send_email(method, mailer)
159
185
  config = sorcery_config
160
186
  mail = config.send(mailer).send(config.send(method),self)
161
- if defined?(ActionMailer) and config.send(mailer).superclass == ActionMailer::Base
187
+ if defined?(ActionMailer) and config.send(mailer).kind_of?(Class) and config.send(mailer) < ActionMailer::Base
162
188
  mail.deliver
163
189
  end
164
190
  end
165
191
  end
166
192
 
167
193
  # Each class which calls 'activate_sorcery!' receives an instance of this class.
168
- # Every submodule which gets loaded may add accessors to this class so that all
194
+ # Every submodule which gets loaded may add accessors to this class so that all
169
195
  # options will be configured from a single place.
170
196
  class Config
171
197
 
172
198
  attr_accessor :username_attribute_names, # change default username attribute, for example, to use :email
173
199
  # as the login.
174
-
200
+
175
201
  :password_attribute_name, # change *virtual* password attribute, the one which is used
176
202
  # until an encrypted one is generated.
177
-
203
+
178
204
  :email_attribute_name, # change default email attribute.
205
+
206
+ :downcase_username_before_authenticating, # downcase the username before trying to authenticate, default is false
207
+
179
208
  :crypted_password_attribute_name, # change default crypted_password attribute.
180
209
  :salt_join_token, # what pattern to use to join the password with the salt
181
210
  :salt_attribute_name, # change default salt attribute.
182
211
  :stretches, # how many times to apply encryption to the password.
183
212
  :encryption_key, # encryption key used to encrypt reversible encryptions such as
184
213
  # AES256.
185
-
214
+
186
215
  :subclasses_inherit_config, # make this configuration inheritable for subclasses. Useful for
187
216
  # ActiveRecord's STI.
188
-
217
+
189
218
  :submodules, # configured in config/application.rb
190
219
  :before_authenticate, # an array of method names to call before authentication
191
220
  # completes. used internally.
192
-
221
+
193
222
  :after_config # an array of method names to call after configuration by user.
194
223
  # used internally.
195
-
224
+
196
225
  attr_reader :encryption_provider, # change default encryption_provider.
197
226
  :custom_encryption_provider, # use an external encryption class.
198
227
  :encryption_algorithm # encryption algorithm name. See 'encryption_algorithm=' below
@@ -203,6 +232,7 @@ module Sorcery
203
232
  :@submodules => [],
204
233
  :@username_attribute_names => [:username],
205
234
  :@password_attribute_name => :password,
235
+ :@downcase_username_before_authenticating => false,
206
236
  :@email_attribute_name => :email,
207
237
  :@crypted_password_attribute_name => :crypted_password,
208
238
  :@encryption_algorithm => :bcrypt,
@@ -217,26 +247,26 @@ module Sorcery
217
247
  :@after_config => []
218
248
  }
219
249
  reset!
220
- end
221
-
250
+ end
251
+
222
252
  # Resets all configuration options to their default values.
223
253
  def reset!
224
254
  @defaults.each do |k,v|
225
255
  instance_variable_set(k,v)
226
- end
256
+ end
227
257
  end
228
-
258
+
229
259
  def username_attribute_names=(fields)
230
260
  @username_attribute_names = fields.kind_of?(Array) ? fields : [fields]
231
261
  end
232
-
262
+
233
263
  def custom_encryption_provider=(provider)
234
264
  @custom_encryption_provider = @encryption_provider = provider
235
265
  end
236
-
266
+
237
267
  def encryption_algorithm=(algo)
238
268
  @encryption_algorithm = algo
239
- @encryption_provider = case @encryption_algorithm
269
+ @encryption_provider = case @encryption_algorithm.to_sym
240
270
  when :none then nil
241
271
  when :md5 then CryptoProviders::MD5
242
272
  when :sha1 then CryptoProviders::SHA1
@@ -245,10 +275,11 @@ module Sorcery
245
275
  when :aes256 then CryptoProviders::AES256
246
276
  when :bcrypt then CryptoProviders::BCrypt
247
277
  when :custom then @custom_encryption_provider
278
+ else raise ArgumentError.new("Encryption algorithm supplied, #{algo}, is invalid")
248
279
  end
249
280
  end
250
-
281
+
251
282
  end
252
-
283
+
253
284
  end
254
- end
285
+ end
@@ -3,6 +3,8 @@ require 'fileutils'
3
3
  namespace :sorcery do
4
4
  desc "Adds sorcery's initializer file"
5
5
  task :bootstrap do
6
+ warn "This task is obsolete.\nUse \"rails g sorcery:install\" now.\nSee README for more information."
7
+
6
8
  src = File.join(File.dirname(__FILE__), '..', 'initializers', 'initializer.rb')
7
9
  target = File.join(Rails.root, "config", "initializers", "sorcery.rb")
8
10
  FileUtils.cp(src, target)
data/lib/sorcery.rb CHANGED
@@ -5,6 +5,7 @@ module Sorcery
5
5
  module Adapters
6
6
  autoload :ActiveRecord, 'sorcery/model/adapters/active_record'
7
7
  autoload :Mongoid, 'sorcery/model/adapters/mongoid'
8
+ autoload :MongoMapper, 'sorcery/model/adapters/mongo_mapper'
8
9
  end
9
10
  module Submodules
10
11
  autoload :UserActivation, 'sorcery/model/submodules/user_activation'
@@ -33,12 +34,13 @@ module Sorcery
33
34
  autoload :Twitter, 'sorcery/controller/submodules/external/providers/twitter'
34
35
  autoload :Facebook, 'sorcery/controller/submodules/external/providers/facebook'
35
36
  autoload :Github, 'sorcery/controller/submodules/external/providers/github'
37
+ autoload :Google, 'sorcery/controller/submodules/external/providers/google'
38
+ autoload :Liveid, 'sorcery/controller/submodules/external/providers/liveid'
39
+ autoload :Linkedin, 'sorcery/controller/submodules/external/providers/linkedin'
40
+ autoload :Vkontakte, 'sorcery/controller/submodules/external/providers/vkontakte'
36
41
  end
37
42
  end
38
43
  end
39
- module Adapters
40
- autoload :Sinatra, 'sorcery/controller/adapters/sinatra'
41
- end
42
44
  end
43
45
  module CryptoProviders
44
46
  autoload :Common, 'sorcery/crypto_providers/common'
@@ -52,12 +54,9 @@ module Sorcery
52
54
  autoload :TestHelpers, 'sorcery/test_helpers'
53
55
  module TestHelpers
54
56
  autoload :Rails, 'sorcery/test_helpers/rails'
55
- autoload :Sinatra, 'sorcery/test_helpers/sinatra'
56
57
  autoload :Internal, 'sorcery/test_helpers/internal'
57
58
  module Internal
58
59
  autoload :Rails, 'sorcery/test_helpers/internal/rails'
59
- autoload :Sinatra, 'sorcery/test_helpers/internal/sinatra'
60
- autoload :SinatraModular, 'sorcery/test_helpers/internal/sinatra_modular'
61
60
  end
62
61
 
63
62
  end
@@ -77,6 +76,9 @@ module Sorcery
77
76
  end
78
77
  end
79
78
 
80
- require 'sorcery/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
81
- require 'sorcery/sinatra' if defined?(Sinatra)
79
+ if defined?(MongoMapper)
80
+ MongoMapper::Document.send(:plugin, Sorcery::Model::Adapters::MongoMapper)
81
+ end
82
+
83
+ require 'sorcery/engine' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
82
84
  end